6de1b08aa3
Implementa capacidad para subir archivos a inputs de tipo file. Incluye: - UploadFile() y UploadFiles() para uno o múltiples archivos - Validación de existencia de archivos - Conversión automática a paths absolutos - ClearFileInput() para limpiar - GetFileInputValue() para obtener nombres seleccionados - IsFileInputMultiple() para verificar atributo multiple Usa CDP DOM.setFileInputFiles. Archivo: pkg/browser/upload.go
154 lines
3.7 KiB
Go
154 lines
3.7 KiB
Go
package browser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// UploadFile sube un archivo a un input de tipo file
|
|
func (b *Browser) UploadFile(ctx context.Context, selector string, filePath string) error {
|
|
return b.UploadFiles(ctx, selector, []string{filePath})
|
|
}
|
|
|
|
// UploadFiles sube múltiples archivos a un input de tipo file
|
|
func (b *Browser) UploadFiles(ctx context.Context, selector string, filePaths []string) error {
|
|
// Validar que todos los archivos existen
|
|
var absolutePaths []string
|
|
for _, path := range filePaths {
|
|
// Convertir a path absoluto
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid file path %s: %w", path, err)
|
|
}
|
|
|
|
// Verificar que existe
|
|
if _, err := os.Stat(absPath); os.IsNotExist(err) {
|
|
return fmt.Errorf("file does not exist: %s", absPath)
|
|
}
|
|
|
|
absolutePaths = append(absolutePaths, absPath)
|
|
}
|
|
|
|
// Obtener el nodeId del input
|
|
nodeID, err := b.querySelector(ctx, selector)
|
|
if err != nil {
|
|
return fmt.Errorf("file input not found: %w", err)
|
|
}
|
|
|
|
// Verificar que es un input de tipo file
|
|
script := fmt.Sprintf(`
|
|
(() => {
|
|
const input = document.querySelector('%s');
|
|
return input && input.tagName === 'INPUT' && input.type === 'file';
|
|
})()
|
|
`, selector)
|
|
|
|
result, err := b.Evaluate(ctx, script)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
isFileInput, ok := result.Value.(bool)
|
|
if !ok || !isFileInput {
|
|
return fmt.Errorf("element is not a file input: %s", selector)
|
|
}
|
|
|
|
// Establecer archivos usando CDP
|
|
params := map[string]interface{}{
|
|
"files": absolutePaths,
|
|
"nodeId": nodeID,
|
|
}
|
|
|
|
if err := b.cdpClient.Execute(ctx, "DOM.setFileInputFiles", params, nil); err != nil {
|
|
return fmt.Errorf("failed to set files: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetFileInput es un alias de UploadFiles
|
|
func (b *Browser) SetFileInput(ctx context.Context, selector string, files []string) error {
|
|
return b.UploadFiles(ctx, selector, files)
|
|
}
|
|
|
|
// ClearFileInput limpia un input de tipo file
|
|
func (b *Browser) ClearFileInput(ctx context.Context, selector string) error {
|
|
nodeID, err := b.querySelector(ctx, selector)
|
|
if err != nil {
|
|
return fmt.Errorf("file input not found: %w", err)
|
|
}
|
|
|
|
// Establecer array vacío
|
|
params := map[string]interface{}{
|
|
"files": []string{},
|
|
"nodeId": nodeID,
|
|
}
|
|
|
|
if err := b.cdpClient.Execute(ctx, "DOM.setFileInputFiles", params, nil); err != nil {
|
|
return fmt.Errorf("failed to clear files: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetFileInputValue obtiene los nombres de archivos seleccionados
|
|
func (b *Browser) GetFileInputValue(ctx context.Context, selector string) ([]string, error) {
|
|
script := fmt.Sprintf(`
|
|
(() => {
|
|
const input = document.querySelector('%s');
|
|
if (!input || input.type !== 'file') return null;
|
|
|
|
const files = Array.from(input.files);
|
|
return files.map(f => f.name);
|
|
})()
|
|
`, selector)
|
|
|
|
result, err := b.Evaluate(ctx, script)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result.Value == nil {
|
|
return []string{}, nil
|
|
}
|
|
|
|
// Convertir resultado a []string
|
|
filesInterface, ok := result.Value.([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected result type")
|
|
}
|
|
|
|
var files []string
|
|
for _, fileInterface := range filesInterface {
|
|
if fileName, ok := fileInterface.(string); ok {
|
|
files = append(files, fileName)
|
|
}
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
// IsFileInputMultiple verifica si un input acepta múltiples archivos
|
|
func (b *Browser) IsFileInputMultiple(ctx context.Context, selector string) (bool, error) {
|
|
script := fmt.Sprintf(`
|
|
(() => {
|
|
const input = document.querySelector('%s');
|
|
return input && input.multiple === true;
|
|
})()
|
|
`, selector)
|
|
|
|
result, err := b.Evaluate(ctx, script)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
isMultiple, ok := result.Value.(bool)
|
|
if !ok {
|
|
return false, nil
|
|
}
|
|
|
|
return isMultiple, nil
|
|
}
|