From a2cd234cec9a29a75f23cb61a1d5f23d4dea9873 Mon Sep 17 00:00:00 2001 From: Rajesh Kantipudi <44539669+iamrajeshk@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:29:35 +0530 Subject: [PATCH 1/2] Add conflict resolution options to workspace settings --- components/settings/panel/Apps.vue | 31 ++++++++++++++++++++++++++++++ types/workspaces.ts | 2 ++ 2 files changed, 33 insertions(+) diff --git a/components/settings/panel/Apps.vue b/components/settings/panel/Apps.vue index 659cd4a..9a8a7d0 100644 --- a/components/settings/panel/Apps.vue +++ b/components/settings/panel/Apps.vue @@ -31,6 +31,35 @@ Publish this workspace for external apps + +
+

During conflicts

+
+ +
+
+ +

@@ -192,6 +221,7 @@ const longFormQuestSchema = ref(); const longFormQuestType = ref(longFormQuestSettings.type); const longFormQuestDef = ref(longFormQuestSettings.definition); const longFormQuestUrl = ref(longFormQuestSettings.url); +const workspaceOverrideConflicts = ref(Boolean(workspace.overrideConflicts)); const longFormQuestError = ref(null); const isDraggingQuest = ref(false); @@ -262,6 +292,7 @@ async function saveExternalAppConfiguration() { await Promise.all([ workspacesClient.updateWorkspace(workspace.id, { externalAppAccess: workspace.externalAppAccess, + overrideConflicts: workspaceOverrideConflicts.value, }), workspacesClient.saveLongFormQuestSettings(workspace.id, { type, diff --git a/types/workspaces.ts b/types/workspaces.ts index 6b9cc3a..b36dfe4 100644 --- a/types/workspaces.ts +++ b/types/workspaces.ts @@ -48,6 +48,7 @@ export interface Workspace { externalAppAccess: WorkspaceAppAccess; kartaViewToken?: string; autoFlagReview?: boolean; + overrideConflicts?: boolean; role?: WorkspaceRole; center?: WorkspaceCenter; projectsCount?: number | null; @@ -72,6 +73,7 @@ export interface WorkspacePatch { description?: string; externalAppAccess?: WorkspaceAppAccess; autoFlagReview?: boolean; + overrideConflicts?: boolean; } export type QuestSettingsType = 'NONE' | 'JSON' | 'URL'; From 826954ada156f66731f492f1abd973a905a93260 Mon Sep 17 00:00:00 2001 From: Rajesh Kantipudi <44539669+iamrajeshk@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:04:14 +0530 Subject: [PATCH 2/2] Add tests for conflict resolution options in workspace settings --- components/settings/panel/Apps.vue | 2 +- pages/workspace/[id]/settings.vue | 2 + test/e2e/settings.spec.ts | 65 +++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/components/settings/panel/Apps.vue b/components/settings/panel/Apps.vue index 9a8a7d0..a82b831 100644 --- a/components/settings/panel/Apps.vue +++ b/components/settings/panel/Apps.vue @@ -31,7 +31,7 @@ Publish this workspace for external apps - +

During conflicts

diff --git a/pages/workspace/[id]/settings.vue b/pages/workspace/[id]/settings.vue index e00b52d..0bbe9d7 100644 --- a/pages/workspace/[id]/settings.vue +++ b/pages/workspace/[id]/settings.vue @@ -6,6 +6,8 @@ // confirmation and sends the proper API call to the server (Swagger here: https://new-api.workspaces-stage.sidewalks.washington.edu/openapi.json) // @test e2e: Under "External Apps", turning on "Publish this workspace" enables the other buttons and when clicking "Save" shows a confirmation and sends // the proper API call. +// @test e2e: Under "External Apps" > "During conflicts", choosing "Override" (vs. the default "Resolve") and clicking "Save" sends the proper +// API call with the chosen overrideConflicts value. // @test e2e: While External Apps settings are being saved, the Save button is disabled and displays "Saving..." to prevent duplicate submissions. // @test e2e: the "Custom Imagery" box is validated against the JSON schema here (https://raw.githubusercontent.com/TaskarCenterAtUW/asr-imagery-list/refs/heads/main/schema/schema.json), // and a toast shown when it passes and the API call to set its value is successful on the backend. diff --git a/test/e2e/settings.spec.ts b/test/e2e/settings.spec.ts index d72de8d..46abcd4 100644 --- a/test/e2e/settings.spec.ts +++ b/test/e2e/settings.spec.ts @@ -177,7 +177,9 @@ test.describe('workspace settings', () => { if (req.method() === 'GET') { // Apps.vue binds the checkbox with :true-value="1"; seed externalAppAccess // as 1 (published) so it renders checked, matching the true-value. - return route.fulfill({ json: { ...aWorkspace, externalAppAccess: 1 } }); + // overrideConflicts seeded true; this test doesn't touch the "During + // conflicts" radios, so the value should pass through unchanged. + return route.fulfill({ json: { ...aWorkspace, externalAppAccess: 1, overrideConflicts: true } }); } if (req.method() === 'PATCH') { patchBody = JSON.parse(req.postData() ?? '{}'); @@ -209,8 +211,9 @@ test.describe('workspace settings', () => { // Outline: a confirmation is shown (the page renders a success toast). await expect(successToast(page)).toBeVisible(); - // Proper API call: PATCH workspaces/1 with externalAppAccess set to 0 (off). - expect(patchBody).toEqual({ externalAppAccess: 0 }); + // Proper API call: PATCH workspaces/1 with externalAppAccess set to 0 (off), + // plus the unchanged overrideConflicts value seeded above. + expect(patchBody).toEqual({ externalAppAccess: 0, overrideConflicts: true }); }); // @test e2e: While External Apps settings are being saved, the Save button is @@ -262,7 +265,9 @@ test.describe('workspace settings', () => { await page.route('**/workspaces/1', (route) => { const req = route.request(); if (req.method() === 'GET') { - return route.fulfill({ json: { ...aWorkspace, externalAppAccess: 0 } }); + // overrideConflicts seeded false; this test doesn't touch the "During + // conflicts" radios, so the value should pass through unchanged. + return route.fulfill({ json: { ...aWorkspace, externalAppAccess: 0, overrideConflicts: false } }); } if (req.method() === 'PATCH') { patchBody = JSON.parse(req.postData() ?? '{}'); @@ -298,8 +303,56 @@ test.describe('workspace settings', () => { await apps.getByRole('button', { name: 'Save' }).click(); await expect(successToast(page)).toBeVisible(); - // Proper API call: externalAppAccess turned on (enabled === 1). - expect(patchBody).toEqual({ externalAppAccess: 1 }); + // Proper API call: externalAppAccess turned on (enabled === 1), plus the + // unchanged overrideConflicts value seeded above. + expect(patchBody).toEqual({ externalAppAccess: 1, overrideConflicts: false }); + }); + + // @test e2e: Under "External Apps" > "During conflicts", choosing "Override" (vs. the default + // "Resolve") and clicking "Save" sends the proper API call with the chosen value. + test('choosing "Override" under During Conflicts and saving sends overrideConflicts: true', async ({ page }) => { + await seedAuthenticatedSession(page); + let patchBody: unknown = null; + // Published, with overrideConflicts seeded false so "Resolve" starts selected. + await page.route('**/workspaces/1', (route) => { + const req = route.request(); + if (req.method() === 'GET') { + return route.fulfill({ json: { ...aWorkspace, externalAppAccess: 1, overrideConflicts: false } }); + } + if (req.method() === 'PATCH') { + patchBody = JSON.parse(req.postData() ?? '{}'); + return route.fulfill({ status: 204, body: '' }); + } + return route.fallback(); + }); + await page.route('**/workspaces/1/quests/long/settings', (route) => { + if (route.request().method() === 'GET') { + return route.fulfill({ json: emptyQuestSettings }); + } + return route.fulfill({ status: 204, body: '' }); + }); + await page.route('**/workspaces/1/imagery/settings', route => + route.fulfill({ json: emptyImagerySettings }) + ); + await page.route(/(?:imagery-schema|schema)\.json$/, route => + route.fulfill({ json: imagerySchema }) + ); + + await page.goto('/workspace/1/settings'); + + const apps = page.locator('form.card', { hasText: 'External Apps' }); + const resolve = apps.getByLabel('Resolve'); + const override = apps.getByLabel('Override'); + // Seeded overrideConflicts: false, so "Resolve" starts selected. + await expect(resolve).toBeChecked(); + await expect(override).not.toBeChecked(); + + await override.check(); + await apps.getByRole('button', { name: 'Save' }).click(); + + await expect(successToast(page)).toBeVisible(); + // Proper API call: PATCH workspaces/1 with the chosen overrideConflicts value. + expect(patchBody).toEqual({ externalAppAccess: 1, overrideConflicts: true }); }); // @test e2e: the "Custom Imagery" box is validated against the JSON schema, and a toast shown