diff --git a/client-v3/.gitignore b/client-v3/.gitignore index a751eb35..c60cb229 100644 --- a/client-v3/.gitignore +++ b/client-v3/.gitignore @@ -4,4 +4,5 @@ dist-electron/ junit/ coverage/ *.backup -components.d.ts \ No newline at end of file +components.d.ts +public/docs \ No newline at end of file diff --git a/client-v3/e2e/tests/08-show-config-cues.spec.ts b/client-v3/e2e/tests/08-show-config-cues.spec.ts index 102b0f6c..37d96171 100644 --- a/client-v3/e2e/tests/08-show-config-cues.spec.ts +++ b/client-v3/e2e/tests/08-show-config-cues.spec.ts @@ -109,6 +109,16 @@ test('can open the Go to Page dialog in cue editor', async () => { await waitForModalClosed(page); }); +test('Go to Page submits and navigates to the requested page', async () => { + await page.click('button:has-text("Go to Page")'); + await waitForModal(page, 'Go to Page'); + await page.fill('.modal.show input[type="number"]', '1'); + await confirmModal(page); + // If the fix is working, clicking OK closes the modal; previously Vuelidate prevented this + await waitForModalClosed(page); + await expect(page.locator('p.mb-0:has-text("Current Page: 1")')).toBeVisible(); +}); + // ── Cue Counts ──────────────────────────────────────────────────────────── test('switches to Cue Counts sub-tab', async () => { diff --git a/client-v3/e2e/tests/10-show-config-script.spec.ts b/client-v3/e2e/tests/10-show-config-script.spec.ts index e2b92d04..f942e51f 100644 --- a/client-v3/e2e/tests/10-show-config-script.spec.ts +++ b/client-v3/e2e/tests/10-show-config-script.spec.ts @@ -254,6 +254,55 @@ test('edits the cue identifier', async () => { }); }); +test('can add a cue using Enter key in Add New Cue modal', async () => { + await page.locator('.add-cue-btn').first().click(); + await waitForModal(page, 'Add New Cue'); + await page.locator('.modal.show select#new-cue-type').selectOption({ index: 1 }); + await page.fill('#new-cue-ident', '003'); + // Enter key submits the form (fix: BForm @submit bound to onSubmitNew) + await page.locator('#new-cue-ident').press('Enter'); + await waitForModalClosed(page); + await expect(page.locator('.cue-button:not(.add-cue-btn)')).toHaveCount(2, { timeout: 5_000 }); +}); + +test('can edit a cue identifier using Enter key in Edit Cue modal', async () => { + // Click the second cue button (003) + await page.locator('.cue-button:not(.add-cue-btn)').last().click(); + await waitForModal(page, 'Edit Cue'); + await page.fill('#edit-cue-ident', '004'); + // Enter key submits the form (fix: BForm @submit bound to onSubmitEdit) + await page.locator('#edit-cue-ident').press('Enter'); + await waitForModalClosed(page); + await expect(page.locator('.cue-button:not(.add-cue-btn)')).toHaveCount(2, { timeout: 5_000 }); +}); + +test('Jump to Cue navigates and auto-closes the modal', async () => { + await page.click('button:has-text("Go to Cue")'); + await waitForModal(page, 'Jump to Cue'); + await page.locator('.modal.show select').selectOption({ index: 1 }); // LX type + await page.locator('.modal.show input').fill('004'); + await confirmModal(page); // Click Search + // Single exact match → navigateToMatch() now calls modal.hide() (fix) + await waitForModalClosed(page); + await expect(page.locator('.v-toast__text').filter({ hasText: /Jumped to/ })).toBeVisible({ + timeout: 5_000, + }); +}); + +test('deletes the Enter-key-added cue', async () => { + // Removes the LX 004 cue created in the Enter key test, leaving only LX 002 + await page.locator('.cue-button:not(.add-cue-btn)').last().click(); + await waitForModal(page, 'Edit Cue'); + await page.locator('.modal.show button:has-text("Delete")').click(); + await waitForModal(page, 'Delete Cue'); + await page + .locator('.modal.show') + .filter({ has: page.locator('.modal-title:has-text("Delete Cue")') }) + .locator('.modal-footer button.btn-danger') + .click(); + await expect(page.locator('.cue-button:not(.add-cue-btn)')).toHaveCount(1, { timeout: 5_000 }); +}); + test('deletes the cue', async () => { await page.locator('.cue-button:not(.add-cue-btn)').first().click(); await waitForModal(page, 'Edit Cue'); diff --git a/client-v3/package-lock.json b/client-v3/package-lock.json index 2c82034c..f2f1c006 100644 --- a/client-v3/package-lock.json +++ b/client-v3/package-lock.json @@ -1,12 +1,12 @@ { "name": "client-v3", - "version": "0.32.0", + "version": "0.32.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "client-v3", - "version": "0.32.0", + "version": "0.32.1", "dependencies": { "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", @@ -19,7 +19,7 @@ "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "deep-object-diff": "1.1.9", - "dompurify": "^3.4.9", + "dompurify": "^3.4.10", "fuse.js": "^7.4.2", "lodash": "^4.18.1", "loglevel": "^1.9.2", @@ -27,7 +27,7 @@ "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "splitpanes": "^4.1.2", - "vue": "^3.5.35", + "vue": "^3.5.38", "vue-multiselect": "^3.5.0", "vue-router": "^5.1.0", "vue-toast-notification": "^3.1.3" @@ -53,7 +53,7 @@ "jiti": "^2.7.0", "jsdom": "^29.1.1", "prettier": "^3.8.4", - "sass": "1.100.0", + "sass": "1.101.0", "typescript": "^6.0.3", "typescript-eslint": "^8.61.0", "unplugin-icons": "^23.0.1", @@ -299,9 +299,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.2.tgz", - "integrity": "sha512-n6Zd8mpVhObnaOqq6TC/lBCKgYncAGfFwuvJGQZTDRAwEoxwXIZu9kXBQeXkcqHsE6Sp6LyxDMrvXj5gOxnryw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.3.tgz", + "integrity": "sha512-DOgvIPkikIOixQRlD4YF31VN6fLLUTdrzhfRbis8vm0kMTgIbEPX0Ip/YX9fOeV9iywAS4sUUbTclpan7yYP8Q==", "dev": true, "funding": [ { @@ -1608,9 +1608,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", - "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", + "version": "25.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", + "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", "dev": true, "license": "MIT", "peer": true, @@ -2056,40 +2056,40 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", - "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz", + "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.3", - "@vue/shared": "3.5.35", + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.38", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", - "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz", + "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-core": "3.5.35", - "@vue/shared": "3.5.35" + "@vue/compiler-core": "3.5.38", + "@vue/shared": "3.5.38" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", - "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz", + "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.3", - "@vue/compiler-core": "3.5.35", - "@vue/compiler-dom": "3.5.35", - "@vue/compiler-ssr": "3.5.35", - "@vue/shared": "3.5.35", + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.38", + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.15", @@ -2097,13 +2097,13 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", - "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz", + "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.35", - "@vue/shared": "3.5.35" + "@vue/compiler-dom": "3.5.38", + "@vue/shared": "3.5.38" } }, "node_modules/@vue/devtools-api": { @@ -2140,54 +2140,54 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", - "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz", + "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.35" + "@vue/shared": "3.5.38" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", - "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz", + "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.35", - "@vue/shared": "3.5.35" + "@vue/reactivity": "3.5.38", + "@vue/shared": "3.5.38" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", - "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz", + "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.35", - "@vue/runtime-core": "3.5.35", - "@vue/shared": "3.5.35", + "@vue/reactivity": "3.5.38", + "@vue/runtime-core": "3.5.38", + "@vue/shared": "3.5.38", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", - "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz", + "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.5.35", - "@vue/shared": "3.5.35" + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38" }, "peerDependencies": { - "vue": "3.5.35" + "vue": "3.5.38" } }, "node_modules/@vue/shared": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", - "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz", + "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -2348,9 +2348,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "license": "MIT", "peer": true, "bin": { @@ -2939,9 +2939,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.9.tgz", - "integrity": "sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.10.tgz", + "integrity": "sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -4377,9 +4377,9 @@ } }, "node_modules/obug": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz", - "integrity": "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -4679,9 +4679,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.2.tgz", - "integrity": "sha512-Wjvt4scRFouioIInHf51IFNP4ltJ2EngJM+cZPGiqbKetBfmP3vpdPV8ID2S6JS6/jdo74N8+aEYH9lQr2C6sA==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", "dev": true, "license": "MIT", "dependencies": { @@ -4854,9 +4854,9 @@ } }, "node_modules/sass": { - "version": "1.100.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz", - "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==", + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", "dev": true, "license": "MIT", "peer": true, @@ -5661,17 +5661,17 @@ } }, "node_modules/vue": { - "version": "3.5.35", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", - "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz", + "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.35", - "@vue/compiler-sfc": "3.5.35", - "@vue/runtime-dom": "3.5.35", - "@vue/server-renderer": "3.5.35", - "@vue/shared": "3.5.35" + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-sfc": "3.5.38", + "@vue/runtime-dom": "3.5.38", + "@vue/server-renderer": "3.5.38", + "@vue/shared": "3.5.38" }, "peerDependencies": { "typescript": "*" diff --git a/client-v3/package.json b/client-v3/package.json index a389ea89..0b5c094c 100644 --- a/client-v3/package.json +++ b/client-v3/package.json @@ -1,6 +1,6 @@ { "name": "client-v3", - "version": "0.32.0", + "version": "0.32.1", "description": "DigiScript front end (Vue 3)", "author": "DreamTeamProd", "private": true, @@ -42,7 +42,7 @@ "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "deep-object-diff": "1.1.9", - "dompurify": "^3.4.9", + "dompurify": "^3.4.10", "fuse.js": "^7.4.2", "lodash": "^4.18.1", "loglevel": "^1.9.2", @@ -50,7 +50,7 @@ "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "splitpanes": "^4.1.2", - "vue": "^3.5.35", + "vue": "^3.5.38", "vue-multiselect": "^3.5.0", "vue-router": "^5.1.0", "vue-toast-notification": "^3.1.3" @@ -75,7 +75,7 @@ "jiti": "^2.7.0", "jsdom": "^29.1.1", "prettier": "^3.8.4", - "sass": "1.100.0", + "sass": "1.101.0", "typescript": "^6.0.3", "typescript-eslint": "^8.61.0", "unplugin-icons": "^23.0.1", diff --git a/client-v3/src/components/show/config/cues/CueEditor.vue b/client-v3/src/components/show/config/cues/CueEditor.vue index bf179840..03e5fafd 100644 --- a/client-v3/src/components/show/config/cues/CueEditor.vue +++ b/client-v3/src/components/show/config/cues/CueEditor.vue @@ -57,22 +57,11 @@ :no-footer="changingPage" :no-close-on-backdrop="changingPage" :no-close-on-esc="changingPage" - :ok-disabled="pageV$.pageInputFormState.$invalid" @ok.prevent="goToPage" > - + - - - This is a required field, and must be greater than 0. - + @@ -83,10 +72,7 @@