mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-16 15:45:50 +01:00
fix(v3): fix macOS mkdir when APP_NAME contains spaces
Replace brace expansion {MacOS,Resources} with two separate mkdir commands.
Brace expansion doesn't work inside quoted strings and is shell-dependent.
Adds integration test to verify mkdir works with spaces in paths.
342 lines
8.4 KiB
Go
342 lines
8.4 KiB
Go
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/wailsapp/task/v3/taskfile/ast"
|
|
)
|
|
|
|
func TestTaskParameterPassing(t *testing.T) {
|
|
// Skip if running in CI without proper environment
|
|
if os.Getenv("CI") == "true" && os.Getenv("SKIP_INTEGRATION_TESTS") == "true" {
|
|
t.Skip("Skipping integration test in CI")
|
|
}
|
|
|
|
// Create a temporary directory for test
|
|
tmpDir, err := os.MkdirTemp("", "wails-task-test-*")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
// Create a test Taskfile
|
|
taskfileContent := `version: '3'
|
|
|
|
tasks:
|
|
build:
|
|
cmds:
|
|
- echo "PLATFORM={{.PLATFORM | default "default-platform"}}"
|
|
- echo "CONFIG={{.CONFIG | default "default-config"}}"
|
|
silent: true
|
|
|
|
package:
|
|
cmds:
|
|
- echo "VERSION={{.VERSION | default "1.0.0"}}"
|
|
- echo "OUTPUT={{.OUTPUT | default "output.pkg"}}"
|
|
silent: true
|
|
|
|
test:
|
|
cmds:
|
|
- echo "ENV={{.ENV | default "test"}}"
|
|
- echo "FLAGS={{.FLAGS | default "none"}}"
|
|
silent: true
|
|
`
|
|
|
|
taskfilePath := filepath.Join(tmpDir, "Taskfile.yml")
|
|
err = os.WriteFile(taskfilePath, []byte(taskfileContent), 0644)
|
|
require.NoError(t, err)
|
|
|
|
// Save current directory
|
|
originalWd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
defer os.Chdir(originalWd)
|
|
|
|
// Change to test directory
|
|
err = os.Chdir(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
options *RunTaskOptions
|
|
otherArgs []string
|
|
expectedOutput []string
|
|
}{
|
|
{
|
|
name: "Build task with parameters",
|
|
options: &RunTaskOptions{Name: "build"},
|
|
otherArgs: []string{"PLATFORM=linux", "CONFIG=production"},
|
|
expectedOutput: []string{
|
|
"PLATFORM=linux",
|
|
"CONFIG=production",
|
|
},
|
|
},
|
|
{
|
|
name: "Package task with parameters",
|
|
options: &RunTaskOptions{Name: "package"},
|
|
otherArgs: []string{"VERSION=2.5.0", "OUTPUT=myapp.pkg"},
|
|
expectedOutput: []string{
|
|
"VERSION=2.5.0",
|
|
"OUTPUT=myapp.pkg",
|
|
},
|
|
},
|
|
{
|
|
name: "Task with default values",
|
|
options: &RunTaskOptions{Name: "build"},
|
|
otherArgs: []string{},
|
|
expectedOutput: []string{
|
|
"PLATFORM=default-platform",
|
|
"CONFIG=default-config",
|
|
},
|
|
},
|
|
{
|
|
name: "Task with partial parameters",
|
|
options: &RunTaskOptions{Name: "test"},
|
|
otherArgs: []string{"ENV=staging"},
|
|
expectedOutput: []string{
|
|
"ENV=staging",
|
|
"FLAGS=none",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Capture output
|
|
output := captureTaskOutput(t, tt.options, tt.otherArgs)
|
|
|
|
// Verify expected output
|
|
for _, expected := range tt.expectedOutput {
|
|
assert.Contains(t, output, expected, "Output should contain: %s", expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCLIParameterFormats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
otherArgs []string
|
|
expectError bool
|
|
expectedVars map[string]string
|
|
}{
|
|
{
|
|
name: "Standard KEY=VALUE format",
|
|
otherArgs: []string{"build", "KEY1=value1", "KEY2=value2"},
|
|
expectedVars: map[string]string{
|
|
"KEY1": "value1",
|
|
"KEY2": "value2",
|
|
},
|
|
},
|
|
{
|
|
name: "Values with equals signs",
|
|
otherArgs: []string{"build", "URL=https://example.com?key=value", "FORMULA=a=b+c"},
|
|
expectedVars: map[string]string{
|
|
"URL": "https://example.com?key=value",
|
|
"FORMULA": "a=b+c",
|
|
},
|
|
},
|
|
{
|
|
name: "Values with spaces (quoted)",
|
|
otherArgs: []string{"build", "MESSAGE=Hello World", "PATH=/usr/local/bin"},
|
|
expectedVars: map[string]string{
|
|
"MESSAGE": "Hello World",
|
|
"PATH": "/usr/local/bin",
|
|
},
|
|
},
|
|
{
|
|
name: "Mixed valid and invalid arguments",
|
|
otherArgs: []string{"build", "VALID=yes", "invalid-arg", "ANOTHER=value", "--flag"},
|
|
expectedVars: map[string]string{
|
|
"VALID": "yes",
|
|
"ANOTHER": "value",
|
|
},
|
|
},
|
|
{
|
|
name: "Empty value",
|
|
otherArgs: []string{"build", "EMPTY=", "KEY=value"},
|
|
expectedVars: map[string]string{
|
|
"EMPTY": "",
|
|
"KEY": "value",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
call := parseTaskCall(&RunTaskOptions{}, tt.otherArgs)
|
|
|
|
// Verify variables
|
|
for key, expectedValue := range tt.expectedVars {
|
|
var actualValue string
|
|
found := false
|
|
if call.Vars != nil {
|
|
call.Vars.Range(func(k string, v ast.Var) error {
|
|
if k == key {
|
|
actualValue = v.Value.(string)
|
|
found = true
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
assert.True(t, found, "Variable %s not found", key)
|
|
assert.Equal(t, expectedValue, actualValue, "Variable %s mismatch", key)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper function to capture task output
|
|
func captureTaskOutput(t *testing.T, options *RunTaskOptions, otherArgs []string) string {
|
|
// Save original stdout and stderr
|
|
oldStdout := os.Stdout
|
|
oldStderr := os.Stderr
|
|
defer func() {
|
|
os.Stdout = oldStdout
|
|
os.Stderr = oldStderr
|
|
}()
|
|
|
|
// Create pipe to capture output
|
|
r, w, err := os.Pipe()
|
|
require.NoError(t, err)
|
|
|
|
os.Stdout = w
|
|
os.Stderr = w
|
|
|
|
// Run task in a goroutine
|
|
done := make(chan bool)
|
|
var taskErr error
|
|
go func() {
|
|
// Note: This is a simplified version for testing
|
|
// In real tests, you might want to mock the Task executor
|
|
taskErr = RunTask(options, otherArgs)
|
|
w.Close()
|
|
done <- true
|
|
}()
|
|
|
|
// Read output
|
|
var buf bytes.Buffer
|
|
_, err = buf.ReadFrom(r)
|
|
require.NoError(t, err)
|
|
|
|
// Wait for task to complete
|
|
<-done
|
|
|
|
// Check for errors (might be expected in some tests)
|
|
if taskErr != nil && !strings.Contains(taskErr.Error(), "expected") {
|
|
t.Logf("Task error (might be expected): %v", taskErr)
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func TestBackwardCompatibility(t *testing.T) {
|
|
// Test that the old way of calling tasks still works
|
|
tests := []struct {
|
|
name string
|
|
osArgs []string
|
|
expectedTask string
|
|
expectedVars map[string]string
|
|
}{
|
|
{
|
|
name: "Legacy os.Args parsing",
|
|
osArgs: []string{"wails3", "task", "build", "PLATFORM=windows"},
|
|
expectedTask: "build",
|
|
expectedVars: map[string]string{
|
|
"PLATFORM": "windows",
|
|
},
|
|
},
|
|
{
|
|
name: "Legacy with flags before task",
|
|
osArgs: []string{"wails3", "task", "--verbose", "test", "ENV=prod"},
|
|
expectedTask: "test",
|
|
expectedVars: map[string]string{
|
|
"ENV": "prod",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Save original os.Args
|
|
originalArgs := os.Args
|
|
defer func() { os.Args = originalArgs }()
|
|
|
|
os.Args = tt.osArgs
|
|
|
|
// Parse using the backward compatibility path
|
|
call := parseTaskCall(&RunTaskOptions{}, []string{})
|
|
|
|
assert.Equal(t, tt.expectedTask, call.Task)
|
|
|
|
for key, expectedValue := range tt.expectedVars {
|
|
var actualValue string
|
|
if call.Vars != nil {
|
|
call.Vars.Range(func(k string, v ast.Var) error {
|
|
if k == key {
|
|
actualValue = v.Value.(string)
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
assert.Equal(t, expectedValue, actualValue, "Variable %s mismatch", key)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMkdirWithSpacesInPath(t *testing.T) {
|
|
if runtime.GOOS != "darwin" {
|
|
t.Skip("Skipping: macOS app bundle test only applies to darwin")
|
|
}
|
|
if os.Getenv("CI") == "true" && os.Getenv("SKIP_INTEGRATION_TESTS") == "true" {
|
|
t.Skip("Skipping integration test in CI")
|
|
}
|
|
|
|
tmpDir, err := os.MkdirTemp("", "wails task test with spaces-*")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
taskfileContent := `version: '3'
|
|
|
|
vars:
|
|
BIN_DIR: "` + tmpDir + `/bin"
|
|
APP_NAME: "My App"
|
|
|
|
tasks:
|
|
create-bundle:
|
|
cmds:
|
|
- mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS"
|
|
- mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources"
|
|
`
|
|
|
|
taskfilePath := filepath.Join(tmpDir, "Taskfile.yml")
|
|
err = os.WriteFile(taskfilePath, []byte(taskfileContent), 0644)
|
|
require.NoError(t, err)
|
|
|
|
originalWd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
defer os.Chdir(originalWd)
|
|
|
|
err = os.Chdir(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
err = RunTask(&RunTaskOptions{Name: "create-bundle"}, []string{})
|
|
require.NoError(t, err)
|
|
|
|
appContentsDir := filepath.Join(tmpDir, "bin", "My App.app", "Contents")
|
|
|
|
macOSDir := filepath.Join(appContentsDir, "MacOS")
|
|
info, err := os.Stat(macOSDir)
|
|
require.NoError(t, err, "MacOS directory should exist")
|
|
assert.True(t, info.IsDir(), "MacOS should be a directory")
|
|
|
|
resourcesDir := filepath.Join(appContentsDir, "Resources")
|
|
info, err = os.Stat(resourcesDir)
|
|
require.NoError(t, err, "Resources directory should exist")
|
|
assert.True(t, info.IsDir(), "Resources should be a directory")
|
|
}
|