fix: several merge messes

This commit is contained in:
jamesread 2026-06-28 22:15:46 +01:00
parent b823d8d21b
commit e63094e8e8
9 changed files with 136 additions and 43 deletions

View File

@ -52,6 +52,10 @@
<script setup> <script setup>
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue' import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import {
choiceDisplayLabel,
syncStateFromModelValue
} from './choiceComboboxHelpers.js'
const props = defineProps({ const props = defineProps({
id: { id: {
@ -117,23 +121,23 @@ const filteredChoices = computed(() => {
}) })
}) })
watch(() => props.modelValue, () => { watch([() => props.modelValue, () => props.choices], () => {
if (!isOpen.value) { if (!isOpen.value) {
query.value = selectedLabel() syncFromModelValue()
} }
}, { immediate: true }) }, { immediate: true })
function choiceLabel(choice) { function choiceLabel(choice) {
return choice.title || choice.value return choiceDisplayLabel(choice)
} }
function selectedLabel() { function syncFromModelValue() {
const match = props.choices.find(choice => choice.value === props.modelValue) const next = syncStateFromModelValue(props.choices, props.modelValue)
if (!match) { query.value = next.query
return props.modelValue || ''
}
return choiceLabel(match) if (next.modelValue !== props.modelValue) {
emitValue(next.modelValue)
}
} }
function openList() { function openList() {
@ -144,7 +148,7 @@ function openList() {
function closeList() { function closeList() {
isOpen.value = false isOpen.value = false
query.value = selectedLabel() syncFromModelValue()
} }
function emitValue(value) { function emitValue(value) {
@ -153,11 +157,15 @@ function emitValue(value) {
function selectChoice(choice) { function selectChoice(choice) {
emitValue(choice.value) emitValue(choice.value)
closeList() query.value = choiceLabel(choice)
isOpen.value = false
} }
function handleFocus() { function handleFocus() {
query.value = isOpen.value ? query.value : selectedLabel() if (!isOpen.value) {
syncFromModelValue()
}
openList() openList()
} }

View File

@ -0,0 +1,33 @@
export function findSelectedChoice (choices, modelValue) {
if (!modelValue) {
return null
}
return choices.find(choice => choice.value === modelValue) ?? null
}
export function choiceDisplayLabel (choice) {
return choice.title || choice.value
}
export function displayLabelForModelValue (choices, modelValue) {
const match = findSelectedChoice(choices, modelValue)
return match ? choiceDisplayLabel(match) : ''
}
export function normalizedModelValue (choices, modelValue) {
if (!modelValue) {
return ''
}
return findSelectedChoice(choices, modelValue) ? modelValue : ''
}
export function syncStateFromModelValue (choices, modelValue) {
const normalizedValue = normalizedModelValue(choices, modelValue)
return {
query: displayLabelForModelValue(choices, normalizedValue),
modelValue: normalizedValue
}
}

View File

@ -0,0 +1,40 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import {
displayLabelForModelValue,
normalizedModelValue,
syncStateFromModelValue
} from './choiceComboboxHelpers.js'
const choices = [
{ title: 'Production', value: 'prod' },
{ title: 'Staging', value: 'stage' }
]
test('displayLabelForModelValue returns the choice label for valid values', () => {
assert.equal(displayLabelForModelValue(choices, 'prod'), 'Production')
})
test('displayLabelForModelValue clears invalid enum values instead of echoing them', () => {
assert.equal(displayLabelForModelValue(choices, 'missing'), '')
})
test('normalizedModelValue keeps valid values and clears invalid ones', () => {
assert.equal(normalizedModelValue(choices, 'stage'), 'stage')
assert.equal(normalizedModelValue(choices, 'missing'), '')
assert.equal(normalizedModelValue(choices, ''), '')
})
test('syncStateFromModelValue clears invalid selections for closed-state sync', () => {
assert.deepEqual(syncStateFromModelValue(choices, 'missing'), {
query: '',
modelValue: ''
})
})
test('syncStateFromModelValue preserves valid selections for closed-state sync', () => {
assert.deepEqual(syncStateFromModelValue(choices, 'stage'), {
query: 'Staging',
modelValue: 'stage'
})
})

View File

@ -5,6 +5,8 @@ import {
getRootAndWait, getRootAndWait,
getActionButtons, getActionButtons,
takeScreenshotOnFailure, takeScreenshotOnFailure,
waitForArgumentFormPage,
waitForArgumentFormReady,
} from '../../lib/elements.js' } from '../../lib/elements.js'
@ -46,11 +48,13 @@ describe('config: multipleDropdowns', function () {
await button.click() await button.click()
// Wait for navigation to argument form page await waitForArgumentFormPage(8000)
await webdriver.wait(new Condition('wait for argument form page', async () => { await waitForArgumentFormReady(10000)
const url = await webdriver.getCurrentUrl()
return url.includes('/actionBinding/') && url.includes('/argumentForm') await webdriver.wait(new Condition('wait for choice comboboxes', async () => {
}), 8000) const boxes = await webdriver.findElements(By.css('main .choice-combobox'))
return boxes.length >= 2
}), 10000)
const comboboxes = await webdriver.findElements(By.css('main .choice-combobox')) const comboboxes = await webdriver.findElements(By.css('main .choice-combobox'))

View File

@ -20,9 +20,7 @@ compile-x64-lin:
go env -u GOOS go env -u GOOS
compile-x64-win: windows-resources compile-x64-win: windows-resources
go env -w GOOS=windows GOARCH=amd64 GOOS=windows GOARCH=amd64 go build -o OliveTin.exe
go build -o OliveTin.exe
go env -u GOOS GOARCH
windows-resources: windows-resources:
$(MAKE) -wC .. windows-resources $(MAKE) -wC .. windows-resources

View File

@ -89,29 +89,39 @@ var envConfigTests = []struct {
}}, }},
} }
func setTestEnvInput(t *testing.T, input string) {
t.Helper()
if input != "" {
if err := os.Setenv("INPUT", input); err != nil {
t.Fatalf("Setenv INPUT: %v", err)
}
return
}
if err := os.Unsetenv("INPUT"); err != nil {
t.Fatalf("Unsetenv INPUT: %v", err)
}
}
func loadConfigFromYAML(t *testing.T, yamlStr string, cfg *Config) bool {
t.Helper()
k := koanf.New(".")
if err := k.Load(rawbytes.Provider([]byte(yamlStr)), yaml.Parser()); err != nil {
t.Errorf("Error loading YAML: %v", err)
return false
}
return unmarshalRoot(k, cfg)
}
func TestEnvInConfig(t *testing.T) { func TestEnvInConfig(t *testing.T) {
for _, tt := range envConfigTests { for _, tt := range envConfigTests {
cfg := DefaultConfig() cfg := DefaultConfig()
setIfNotEmpty("INPUT", tt.input) setTestEnvInput(t, tt.input)
k := koanf.New(".") if !loadConfigFromYAML(t, tt.yaml, cfg) {
err := k.Load(rawbytes.Provider([]byte(tt.yaml)), yaml.Parser())
if err != nil {
t.Errorf("Error loading YAML: %v", err)
continue
}
if !unmarshalRoot(k, cfg) {
t.Errorf("Error unmarshalling config for env=%q", tt.input) t.Errorf("Error unmarshalling config for env=%q", tt.input)
continue continue
} }
field := tt.selector(cfg) field := tt.selector(cfg)
assert.Equal(t, tt.output, field, assert.Equal(t, tt.output, field,
"Unmarshaled config field doesn't match expected value: env=%q", tt.input) "Unmarshaled config field doesn't match expected value: env=%q", tt.input)
os.Unsetenv("INPUT")
}
}
func setIfNotEmpty(key, val string) {
if val != "" {
os.Setenv(key, val)
} }
} }

View File

@ -24,8 +24,8 @@ normalize_msi_version() {
local raw="${1#v}" local raw="${1#v}"
raw="${raw%%-*}" raw="${raw%%-*}"
if [[ ! "${raw}" =~ ^[0-9]+(\.[0-9]+){0,3}$ ]]; then if [[ ! "${raw}" =~ ^[0-9]+(\.[0-9]+){0,3}$ ]]; then
echo "0.0.0" echo "Invalid MSI version (expected major[.minor[.patch[.build]]]): ${1}" >&2
return return 1
fi fi
local -a parts=() local -a parts=()
IFS='.' read -r -a parts <<<"${raw}" IFS='.' read -r -a parts <<<"${raw}"
@ -43,7 +43,7 @@ if [[ -z "${VERSION}" ]]; then
echo "Could not determine release version; set VERSION explicitly" >&2 echo "Could not determine release version; set VERSION explicitly" >&2
exit 1 exit 1
fi fi
MSI_VERSION="$(normalize_msi_version "${VERSION}")" MSI_VERSION="$(normalize_msi_version "${VERSION}")" || exit 1
STAGING="$(mktemp -d)" STAGING="$(mktemp -d)"
APP_STAGING="$(mktemp -d)" APP_STAGING="$(mktemp -d)"

View File

@ -37,14 +37,14 @@ normalize_windows_version() {
} }
resolve_version() { resolve_version() {
if [[ -n "${VERSION:-}" ]]; then
echo "${VERSION}"
return
fi
if [[ $# -gt 0 && -n "${1:-}" ]]; then if [[ $# -gt 0 && -n "${1:-}" ]]; then
echo "${1}" echo "${1}"
return return
fi fi
if [[ -n "${VERSION:-}" ]]; then
echo "${VERSION}"
return
fi
if git -C "${REPO_ROOT}" describe --tags --abbrev=0 >/dev/null 2>&1; then if git -C "${REPO_ROOT}" describe --tags --abbrev=0 >/dev/null 2>&1; then
git -C "${REPO_ROOT}" describe --tags --abbrev=0 git -C "${REPO_ROOT}" describe --tags --abbrev=0
return return

View File

@ -28,7 +28,7 @@ checksums_path="${DIST_DIR}/checksums.txt"
new_checksum="$(cd "${DIST_DIR}" && sha256sum "${MSI_NAME}")" new_checksum="$(cd "${DIST_DIR}" && sha256sum "${MSI_NAME}")"
if [[ -f "${checksums_path}" ]] && grep -qF " ${MSI_NAME}" "${checksums_path}"; then if [[ -f "${checksums_path}" ]] && grep -qF " ${MSI_NAME}" "${checksums_path}"; then
tmp="$(mktemp)" tmp="$(mktemp)"
grep -vF " ${MSI_NAME}" "${checksums_path}" > "${tmp}" grep -vF " ${MSI_NAME}" "${checksums_path}" > "${tmp}" || true
printf '%s\n' "${new_checksum}" >> "${tmp}" printf '%s\n' "${new_checksum}" >> "${tmp}"
mv "${tmp}" "${checksums_path}" mv "${tmp}" "${checksums_path}"
elif [[ -f "${checksums_path}" ]]; then elif [[ -f "${checksums_path}" ]]; then