Skip to content

Commit aee5ccd

Browse files
committed
Added workflow subsets test and minor improvements
1 parent a5a09cd commit aee5ccd

File tree

4 files changed

+187
-22
lines changed

4 files changed

+187
-22
lines changed

src/lib/components/jobs/ImagesStatus.svelte

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
<span class="d-flex">
2828
{#if status.num_submitted_images > 0}
2929
<button
30+
aria-label="Submitted images"
3031
class="status-modal-btn btn btn-link text-decoration-none p-0"
3132
on:click={() =>
3233
imagesStatusModal.open(projectId, datasetId, workflowTaskId, parametersHash, 'submitted')}
3334
>
3435
<span class="d-flex">
35-
<span class="pe-1 image-status text-primary" aria-label="Submitted images">
36+
<span class="pe-1 image-status text-primary">
3637
{status.num_submitted_images}
3738
</span>
3839
<div
@@ -46,12 +47,13 @@
4647
{/if}
4748
{#if showDone}
4849
<button
50+
aria-label="Done images"
4951
class="status-modal-btn btn btn-link text-decoration-none p-0"
5052
on:click={() =>
5153
imagesStatusModal.open(projectId, datasetId, workflowTaskId, parametersHash, 'done')}
5254
>
5355
<span class="d-flex">
54-
<span class="image-status text-success ps-1" aria-label="Done images">
56+
<span class="image-status text-success ps-1">
5557
{status.num_done_images}
5658
</span>
5759
<i class="image-status-icon bi bi-check text-success pe-1" />
@@ -63,12 +65,13 @@
6365
{/if}
6466
{#if showFailed}
6567
<button
68+
aria-label="Failed images"
6669
class="status-modal-btn btn btn-link text-decoration-none p-0"
6770
on:click={() =>
6871
imagesStatusModal.open(projectId, datasetId, workflowTaskId, parametersHash, 'failed')}
6972
>
7073
<span class="d-flex">
71-
<span class="image-status text-danger ps-1" aria-label="Failed images">
74+
<span class="image-status text-danger ps-1">
7275
{status.num_failed_images}
7376
</span>
7477
<i class="image-status-icon bi bi-x text-danger pe-1" />

src/lib/components/jobs/ImagesStatusModal.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
await loadImages(page, pageSize);
5757
}
5858
59+
function onClose() {
60+
data = undefined;
61+
}
62+
5963
/**
6064
* @param {number} currentPage
6165
* @param {number} selectedPageSize
@@ -116,7 +120,7 @@
116120
}
117121
</script>
118122

119-
<Modal id="imagesStatusModal" bind:this={modal} fullscreen={true} bodyCss="p-0">
123+
<Modal id="imagesStatusModal" bind:this={modal} fullscreen={true} bodyCss="p-0" {onClose}>
120124
<svelte:fragment slot="header">
121125
<h1 class="modal-title fs-5">
122126
{#if selectedLogImage && !loadingLogs}

src/routes/v2/projects/[projectId]/workflows/[workflowId]/+page.svelte

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,10 @@
500500
const submitted = Object.values(statuses).filter((s) => s.num_submitted_images > 0);
501501
if (submitted.length > 0 || selectedSubmittedJob?.status === 'submitted') {
502502
window.clearTimeout(statusWatcherTimer);
503-
statusWatcherTimer = window.setTimeout(loadJobsStatus, updateJobsInterval);
503+
statusWatcherTimer = window.setTimeout(
504+
loadJobsStatus,
505+
submitted.length > 0 ? updateJobsInterval : 0
506+
);
504507
} else {
505508
await reloadSelectedDataset();
506509
selectedSubmittedJob = undefined;
@@ -833,13 +836,15 @@
833836
/>
834837
{:else if expandedWorkflowTaskId === workflowTask.id}
835838
<button
839+
aria-label="Hide subsets"
836840
class="btn btn-link p-0 text-white"
837841
on:click={() => (expandedWorkflowTaskId = undefined)}
838842
>
839843
<i class="bi bi-caret-down-fill" />
840844
</button>
841845
{:else}
842846
<button
847+
aria-label="Show subsets"
843848
class="btn btn-link p-0"
844849
class:text-white={selectedWorkflowTask?.id === workflowTask.id}
845850
on:click={() => loadSubsetStatus(workflowTask.id)}
@@ -1135,22 +1140,22 @@
11351140
<button
11361141
type="button"
11371142
class="btn btn-warning"
1138-
on:click={() => {
1143+
on:click={async () => {
11391144
argsSchemaForm?.discardChanges();
1140-
setSelectedWorkflowTask(preventedSelectedTaskChange);
1141-
selectSubset(preventedSelectedSubsetChange);
1145+
await setSelectedWorkflowTask(preventedSelectedTaskChange);
1146+
await selectSubset(preventedSelectedSubsetChange);
1147+
argsUnsavedChangesModal.hide();
11421148
}}
1143-
data-bs-dismiss="modal"
11441149
>
11451150
Discard changes
11461151
</button>
11471152
<button
11481153
type="button"
11491154
class="btn btn-success"
1150-
on:click={() => {
1151-
argsSchemaForm?.saveChanges();
1155+
on:click={async () => {
1156+
await argsSchemaForm?.saveChanges();
1157+
argsUnsavedChangesModal.hide();
11521158
}}
1153-
data-bs-dismiss="modal"
11541159
>
11551160
Save changes
11561161
</button>
@@ -1171,12 +1176,12 @@
11711176
<button
11721177
type="button"
11731178
class="btn btn-warning"
1174-
on:click={() => {
1179+
on:click={async () => {
11751180
inputFiltersTab?.discardChanges();
1176-
setSelectedWorkflowTask(preventedSelectedTaskChange);
1177-
selectSubset(preventedSelectedSubsetChange);
1181+
await setSelectedWorkflowTask(preventedSelectedTaskChange);
1182+
await selectSubset(preventedSelectedSubsetChange);
1183+
filtersUnsavedChangesModal.hide();
11781184
}}
1179-
data-bs-dismiss="modal"
11801185
>
11811186
Discard changes
11821187
</button>
@@ -1185,8 +1190,8 @@
11851190
class="btn btn-success"
11861191
on:click={async () => {
11871192
await inputFiltersTab?.save();
1193+
filtersUnsavedChangesModal.hide();
11881194
}}
1189-
data-bs-dismiss="modal"
11901195
>
11911196
Save changes
11921197
</button>
@@ -1208,12 +1213,12 @@
12081213
<button
12091214
type="button"
12101215
class="btn btn-warning"
1211-
on:click={() => {
1216+
on:click={async () => {
12121217
metaPropertiesForm?.discardChanges();
1213-
setSelectedWorkflowTask(preventedSelectedTaskChange);
1214-
selectSubset(preventedSelectedSubsetChange);
1218+
await setSelectedWorkflowTask(preventedSelectedTaskChange);
1219+
await selectSubset(preventedSelectedSubsetChange);
1220+
metaPropertiesUnsavedChangesModal.hide();
12151221
}}
1216-
data-bs-dismiss="modal"
12171222
>
12181223
Discard changes
12191224
</button>
@@ -1222,8 +1227,8 @@
12221227
class="btn btn-success"
12231228
on:click={async () => {
12241229
await metaPropertiesForm?.saveChanges();
1230+
metaPropertiesUnsavedChangesModal.hide();
12251231
}}
1226-
data-bs-dismiss="modal"
12271232
>
12281233
Save changes
12291234
</button>

tests/v2/workflow_subsets.spec.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { expect, test } from './workflow_fixture.js';
2+
import { selectSlimSelect, waitModalClosed, waitPageLoading } from '../utils.js';
3+
import { createDataset } from './dataset_utils.js';
4+
import { waitTaskFailure, waitTasksSuccess } from './workflow_task_utils.js';
5+
6+
test('Workflow subsets', async ({ page, workflow }) => {
7+
await page.waitForURL(workflow.url);
8+
await waitPageLoading(page);
9+
10+
test.slow();
11+
const modal = page.locator('.modal.show');
12+
13+
/** @type {string} */
14+
let zarrDir;
15+
await test.step('Create test dataset', async () => {
16+
const dataset = await createDataset(page, workflow.projectId);
17+
zarrDir = dataset.zarrDir;
18+
});
19+
20+
await test.step('Open workflow page and configure tasks', async () => {
21+
await page.goto(workflow.url);
22+
await waitPageLoading(page);
23+
await workflow.addTask('create_ome_zarr_compound');
24+
await workflow.selectTask('create_ome_zarr_compound');
25+
await page.getByRole('textbox', { name: 'Image Dir' }).fill(zarrDir);
26+
await page.getByRole('spinbutton', { name: 'Num Images' }).fill('15');
27+
await workflow.addTask('illumination_correction');
28+
await workflow.addTask('cellpose_segmentation');
29+
await page.getByRole('button', { name: 'Save changes' }).click();
30+
await expect(page.getByRole('button', { name: 'Save changes' })).toBeDisabled();
31+
});
32+
33+
await test.step('Add meta properties', async () => {
34+
await workflow.selectTask('cellpose_segmentation');
35+
await page.getByRole('button', { name: 'Meta', exact: true }).click();
36+
await page.getByRole('button', { name: 'Add property' }).click();
37+
await page.getByPlaceholder('Argument name').fill('k1');
38+
await page.getByPlaceholder('Argument value').fill('v1');
39+
await page.getByRole('button', { name: 'Save changes' }).click();
40+
await expect(page.getByRole('button', { name: 'Save changes' })).toBeDisabled();
41+
});
42+
43+
await test.step('Run workflow', async () => {
44+
await page.getByRole('button', { name: 'Run workflow' }).click();
45+
await modal.waitFor();
46+
await modal.getByRole('button', { name: 'Run' }).click();
47+
await modal.getByRole('button', { name: 'Confirm' }).click();
48+
await waitModalClosed(page);
49+
await waitTasksSuccess(page);
50+
});
51+
52+
await test.step('Open logs modal', async () => {
53+
await page.locator('[aria-label="Done images"]').last().click();
54+
await modal.waitFor();
55+
await expect(modal.getByText("Images with status='done'")).toBeVisible();
56+
await expect(modal.getByText('Total results: 15')).toBeVisible();
57+
await modal.getByRole('button', { name: '2' }).click();
58+
await expect(modal.getByRole('row')).toHaveCount(5);
59+
await modal.getByRole('button', { name: 'Logs' }).last().click();
60+
await expect(modal.getByText('START cellpose_segmentation task')).toBeVisible();
61+
await modal.getByRole('button', { name: 'Back' }).click();
62+
await expect(modal.getByRole('row')).toHaveCount(5);
63+
await modal.getByRole('button', { name: 'Close' }).click();
64+
await waitModalClosed(page);
65+
});
66+
67+
await test.step('Update meta properties', async () => {
68+
await page.getByRole('button', { name: 'Meta', exact: true }).click();
69+
await page.getByPlaceholder('Argument name').fill('k2');
70+
await page.getByPlaceholder('Argument value').fill('v2');
71+
await page.getByRole('button', { name: 'Save changes' }).click();
72+
await expect(page.getByRole('button', { name: 'Save changes' })).toBeDisabled();
73+
});
74+
75+
await test.step('Continue workflow', async () => {
76+
await page.getByRole('button', { name: 'Continue workflow' }).click();
77+
await modal.waitFor();
78+
await modal.getByRole('combobox', { name: 'First task' }).selectOption('cellpose_segmentation');
79+
await modal.getByRole('button', { name: 'Image list' }).click();
80+
await selectSlimSelect(page, page.getByLabel('Selector for attribute well'), 'A02');
81+
await modal.getByRole('button', { name: 'Apply' }).click();
82+
await expect(modal.getByRole('button', { name: 'Apply' })).toBeDisabled();
83+
await modal.getByRole('button', { name: 'Run' }).click();
84+
await expect(modal.getByText('This job will process 1 image')).toBeVisible();
85+
await modal.getByRole('button', { name: 'Confirm' }).click();
86+
await waitModalClosed(page);
87+
await waitTasksSuccess(page);
88+
});
89+
90+
await test.step('Open subsets of last task and check meta', async () => {
91+
await page.getByRole('button', { name: 'Meta', exact: true }).click();
92+
await expect(page.getByPlaceholder('Argument name')).toHaveValue('k2');
93+
await expect(page.getByPlaceholder('Argument value')).toHaveValue('v2');
94+
await expect(page.getByRole('button', { name: 'Add property' })).toBeEnabled();
95+
await page.locator('[aria-label="Show subsets"]').last().click();
96+
await page.getByRole('button', { name: 'Subset 1' }).click();
97+
await expect(page.getByPlaceholder('Argument name')).toHaveValue('k1');
98+
await expect(page.getByPlaceholder('Argument value')).toHaveValue('v1');
99+
await expect(page.getByRole('button', { name: 'Add property' })).not.toBeEnabled();
100+
await page.getByRole('button', { name: 'Subset 2' }).click();
101+
await expect(page.getByPlaceholder('Argument name')).toHaveValue('k2');
102+
await expect(page.getByPlaceholder('Argument value')).toHaveValue('v2');
103+
await expect(page.getByRole('button', { name: 'Add property' })).not.toBeEnabled();
104+
});
105+
106+
await test.step('Open subset logs modal', async () => {
107+
await page.locator('[aria-label="Done images"]').last().click();
108+
await modal.waitFor();
109+
await expect(modal.getByText('Total results: 1')).toBeVisible();
110+
await expect(modal.getByRole('row')).toHaveCount(1);
111+
await modal.getByRole('button', { name: 'Close' }).click();
112+
await waitModalClosed(page);
113+
});
114+
115+
await test.step('Open subsets of second task and check arguments', async () => {
116+
await workflow.selectTask('illumination_correction');
117+
await expect(page.getByRole('button', { name: 'Subset 1' })).not.toBeVisible();
118+
await page.getByRole('button', { name: 'Arguments', exact: true }).click();
119+
await page.getByRole('switch').check();
120+
await page.getByRole('button', { name: 'Save changes' }).click();
121+
await expect(page.getByRole('button', { name: 'Save changes' })).toBeDisabled();
122+
await expect(page.getByRole('switch')).toBeChecked();
123+
await page.locator('[aria-label="Show subsets"]').nth(1).click();
124+
await page.getByRole('button', { name: 'Subset 1' }).click();
125+
await expect(page.getByRole('button', { name: 'Subset 2' })).not.toBeVisible();
126+
await expect(page.getByRole('switch')).not.toBeChecked();
127+
await expect(page.getByRole('switch')).not.toBeEditable();
128+
});
129+
130+
await test.step('Continue workflow causing failure', async () => {
131+
await page.getByRole('button', { name: 'Continue workflow' }).click();
132+
await modal.waitFor();
133+
await modal
134+
.getByRole('combobox', { name: 'First task' })
135+
.selectOption('create_ome_zarr_compound');
136+
await modal.getByRole('button', { name: 'Run' }).click();
137+
await modal.getByRole('button', { name: 'Confirm' }).click();
138+
await waitModalClosed(page);
139+
await waitTaskFailure(page);
140+
});
141+
142+
await test.step('Open failed logs modal', async () => {
143+
await page.locator('[aria-label="Failed images"]').first().click();
144+
await modal.waitFor();
145+
await expect(modal.getByText("Images with status='failed'")).toBeVisible();
146+
await expect(modal.getByText('Total results: 15')).toBeVisible();
147+
await modal.getByRole('button', { name: 'Logs' }).first().click();
148+
await modal.getByRole('button', { name: 'click here to expand' }).click();
149+
await expect(page.getByText('Traceback')).toBeVisible();
150+
await modal.getByRole('button', { name: 'Close' }).click();
151+
await waitModalClosed(page);
152+
});
153+
});

0 commit comments

Comments
 (0)