Skip to content

Commit 908e6ed

Browse files
committed
Added e2e test for task without schema; minor refactoring of duplicated code in workflow tests
1 parent c7bd371 commit 908e6ed

File tree

7 files changed

+173
-73
lines changed

7 files changed

+173
-73
lines changed

tests/admin_jobs.spec.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,28 +124,18 @@ async function createJob(page, inputDataset, outputDataset) {
124124
await workflow.createDataset(outputDataset, 'zarr');
125125
await workflow.createWorkflow();
126126
await page.waitForURL(/** @type {string} */ (workflow.url));
127-
await addTaskToWorkflow(page);
127+
await addTaskToWorkflow(workflow);
128128
await runWorkflow(page, inputDataset, outputDataset);
129129
});
130130
return workflow;
131131
}
132132

133133
/**
134-
* @param {import('@playwright/test').Page} page
134+
* @param {PageWithWorkflow} workflow
135135
*/
136-
async function addTaskToWorkflow(page) {
136+
async function addTaskToWorkflow(workflow) {
137137
await test.step('Add task to workflow', async () => {
138-
await page.locator('[data-bs-target="#insertTaskModal"]').click();
139-
let modalTitle = page.locator('.modal.show .modal-title');
140-
await modalTitle.waitFor();
141-
await expect(modalTitle).toHaveText('New workflow task');
142-
const modal = page.locator('.modal.show');
143-
const selector = modal.getByRole('combobox').first();
144-
await selector.click();
145-
const firstItem = page.getByRole('listbox').locator('[aria-selected="false"]').first();
146-
await firstItem.click();
147-
await page.locator('#taskId').waitFor();
148-
await page.getByRole('button', { name: 'Insert' }).click();
138+
await workflow.addFirstTask();
149139
});
150140
}
151141

tests/experimental_run_workflow.spec.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,7 @@ test('Experimental workflow page', async ({ page, workflow }) => {
99
await waitPageLoading(page);
1010

1111
await test.step('Add task to workflow', async () => {
12-
await page.locator('[data-bs-target="#insertTaskModal"]').click();
13-
let modalTitle = page.locator('.modal.show .modal-title');
14-
await modalTitle.waitFor();
15-
await expect(modalTitle).toHaveText('New workflow task');
16-
const modal = page.locator('.modal.show');
17-
const selector = modal.getByRole('combobox').first();
18-
await selector.click();
19-
const firstItem = page.getByRole('listbox').locator('[aria-selected="false"]').first();
20-
await firstItem.click();
21-
await page.locator('#taskId').waitFor();
22-
await page.getByRole('button', { name: 'Insert' }).click();
12+
await workflow.addFirstTask();
2313
});
2414

2515
await test.step('Select input and output datasets', async () => {

tests/jobs.spec.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,7 @@ test('Execute jobs', async ({ page, workflow }) => {
66
await waitPageLoading(page);
77

88
await test.step('Add task to workflow', async () => {
9-
await page.locator('[data-bs-target="#insertTaskModal"]').click();
10-
let modalTitle = page.locator('.modal.show .modal-title');
11-
await modalTitle.waitFor();
12-
await expect(modalTitle).toHaveText('New workflow task');
13-
14-
const modal = page.locator('.modal.show');
15-
16-
const selector = modal.getByRole('combobox').first();
17-
await selector.click();
18-
const firstItem = page.getByRole('listbox').locator('[aria-selected="false"]').first();
19-
await firstItem.click();
20-
await page.locator('#taskId').waitFor();
21-
await page.getByRole('button', { name: 'Insert' }).click();
9+
await workflow.addFirstTask();
2210
});
2311

2412
await test.step('Run workflow', async () => {

tests/jschema.spec.js

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,7 @@ test('JSON Schema validation', async ({ page, browserName, workflow }) => {
3737
});
3838

3939
await test.step('Add task to workflow', async () => {
40-
await page.goto(workflow.url);
41-
await waitPageLoading(page);
42-
await page.locator('[data-bs-target="#insertTaskModal"]').click();
43-
const modal = page.locator('.modal.show');
44-
await modal.waitFor();
45-
await page.getByText('User tasks').click();
46-
const selector = modal.getByRole('combobox').first();
47-
await selector.click();
48-
const items = await page.getByRole('option').all();
49-
let testTaskItem = null;
50-
for (const item of items) {
51-
const itemText = await item.innerText();
52-
if (itemText.includes(randomTaskName)) {
53-
testTaskItem = item;
54-
break;
55-
}
56-
}
57-
expect(testTaskItem).not.toBeNull();
58-
await /** @type {import('@playwright/test').Locator} */ (testTaskItem).click();
59-
await page.locator('#taskId').waitFor();
60-
await page.getByRole('button', { name: 'Insert' }).click();
61-
await waitModalClosed(page);
40+
await workflow.addTask(randomTaskName);
6241
});
6342

6443
await test.step('Open workflow task form', async () => {

tests/slurm_account.spec.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,7 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
4444
});
4545

4646
await test.step('Add task to workflow', async () => {
47-
await page.locator('[data-bs-target="#insertTaskModal"]').click();
48-
let modalTitle = page.locator('.modal.show .modal-title');
49-
await modalTitle.waitFor();
50-
await expect(modalTitle).toHaveText('New workflow task');
51-
const modal = page.locator('.modal.show');
52-
const selector = modal.getByRole('combobox').first();
53-
await selector.click();
54-
const firstItem = page.getByRole('listbox').locator('[aria-selected="false"]').first();
55-
await firstItem.click();
56-
await page.locator('#taskId').waitFor();
57-
await page.getByRole('button', { name: 'Insert' }).click();
47+
await workflow.addFirstTask();
5848
});
5949

6050
await test.step('Open Run workflow modal', async () => {
@@ -89,5 +79,5 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
8979

9080
await test.step('Wait for job completion', async () => {
9181
await page.getByRole('table').getByText('failed', { exact: true }).waitFor();
92-
})
82+
});
9383
});

tests/task_without_schema.spec.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { expect, test } from './workflow_fixture.js';
2+
import { waitModalClosed, waitPageLoading } from './utils.js';
3+
import path from 'path';
4+
import fs from 'fs';
5+
import os from 'os';
6+
7+
test('Create and edit arguments of a task without schema', async ({ page, workflow }) => {
8+
await page.waitForURL(workflow.url);
9+
await waitPageLoading(page);
10+
11+
await test.step('Open "Add a single task" form', async () => {
12+
await page.goto('/tasks');
13+
await waitPageLoading(page);
14+
await page.getByText('Single task').click();
15+
});
16+
17+
const randomTaskName = Math.random().toString(36).substring(7);
18+
19+
await test.step('Create new task', async () => {
20+
await page.getByRole('textbox', { name: 'Task name' }).fill(randomTaskName);
21+
await page.getByRole('textbox', { name: 'Command' }).fill('/tmp/test');
22+
await page.getByRole('textbox', { name: 'Source' }).fill(`${randomTaskName}-source`);
23+
await page.getByRole('textbox', { name: 'Input Type' }).fill('image');
24+
await page.getByRole('textbox', { name: 'Output Type' }).fill('zarr');
25+
26+
await page.getByRole('button', { name: /^Create$/ }).click();
27+
expect(await page.getByText('field required').count()).toEqual(0);
28+
await page.getByText('Task created successfully').waitFor();
29+
});
30+
31+
await test.step('Open workflow page', async () => {
32+
await page.goto(workflow.url);
33+
await waitPageLoading(page);
34+
});
35+
36+
await test.step('Add task to workflow', async () => {
37+
await workflow.addTask(randomTaskName);
38+
});
39+
40+
await test.step('Add task to workflow again', async () => {
41+
await workflow.addTask(randomTaskName);
42+
});
43+
44+
await test.step('Open workflow task form', async () => {
45+
await page.getByText(`${randomTaskName} #`).first().click();
46+
});
47+
48+
await test.step('Add argument property', async () => {
49+
await page.getByText('Add property').click();
50+
await page.getByPlaceholder('Arg name').fill('key1');
51+
await page.getByPlaceholder('Argument default value').fill('value1');
52+
await page.getByLabel('Save argument').click();
53+
await page.waitForFunction(
54+
() => document.querySelectorAll('[aria-label="Save argument"]').length === 0
55+
);
56+
});
57+
58+
/** @type {string} */
59+
let downloadedFile;
60+
61+
await test.step('Export arguments', async () => {
62+
const exportBtn = page.getByRole('button', { name: 'Export' });
63+
const downloadPromise = page.waitForEvent('download');
64+
await exportBtn.click();
65+
const download = await downloadPromise;
66+
downloadedFile = path.resolve(os.tmpdir(), 'playwright', download.suggestedFilename());
67+
await download.saveAs(downloadedFile);
68+
});
69+
70+
/** @type {object} */
71+
let exportedData;
72+
73+
await test.step('Check exported file', async () => {
74+
exportedData = JSON.parse(fs.readFileSync(downloadedFile).toString());
75+
expect(exportedData.key1).toEqual('value1');
76+
});
77+
78+
await test.step('Import arguments', async () => {
79+
fs.writeFileSync(downloadedFile, JSON.stringify({ key1: 'value1-updated', key2: 'value2' }));
80+
await page.getByRole('button', { name: 'Import' }).click();
81+
const modalTitle = page.locator('.modal.show .modal-title');
82+
await modalTitle.waitFor();
83+
const fileChooserPromise = page.waitForEvent('filechooser');
84+
await page.getByText('Select arguments file').click();
85+
const fileChooser = await fileChooserPromise;
86+
await fileChooser.setFiles(downloadedFile);
87+
await page.getByRole('button', { name: 'Confirm' }).click();
88+
await waitModalClosed(page);
89+
await verifyImportedArguments(page);
90+
});
91+
92+
await test.step('Switch to the other task arguments form, go back to the first form and check the arguments', async () => {
93+
await page.getByText(`${randomTaskName} #`).nth(1).click();
94+
await page.getByText(`${randomTaskName} #`).nth(0).click();
95+
await verifyImportedArguments(page);
96+
});
97+
98+
await test.step('Refresh the page check the arguments', async () => {
99+
await page.reload();
100+
await waitPageLoading(page);
101+
await page.getByText(`${randomTaskName} #`).first().click();
102+
await verifyImportedArguments(page);
103+
});
104+
});
105+
106+
/**
107+
* @param {import('@playwright/test').Page} page
108+
*/
109+
async function verifyImportedArguments(page) {
110+
const values = await page.locator('#args-tab .list-group .list-group-item').allInnerTexts();
111+
expect(values[0]).toEqual('key1');
112+
expect(values[1]).toEqual('value1-updated');
113+
expect(values[2]).toEqual('key2');
114+
expect(values[3]).toEqual('value2');
115+
}

tests/workflow_fixture.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test as baseTest, mergeTests } from '@playwright/test';
2-
import { waitPageLoading } from './utils.js';
2+
import { waitModalClosed, waitPageLoading } from './utils.js';
33
import { PageWithProject } from './project_fixture.js';
44

55
export class PageWithWorkflow extends PageWithProject {
@@ -49,6 +49,52 @@ export class PageWithWorkflow extends PageWithProject {
4949
this.workflowId = match[1];
5050
}
5151
}
52+
53+
async addFirstTask() {
54+
await this.page.locator('[data-bs-target="#insertTaskModal"]').click();
55+
let modalTitle = this.page.locator('.modal.show .modal-title');
56+
await modalTitle.waitFor();
57+
await expect(modalTitle).toHaveText('New workflow task');
58+
const modal = this.page.locator('.modal.show');
59+
const selector = modal.getByRole('combobox').first();
60+
await selector.click();
61+
const firstItem = this.page.getByRole('listbox').locator('[aria-selected="false"]').first();
62+
await firstItem.click();
63+
await this.page.locator('#taskId').waitFor();
64+
await this.page.getByRole('button', { name: 'Insert' }).click();
65+
}
66+
67+
/**
68+
* @param {string} taskName
69+
* @returns {Promise<void>}
70+
*/
71+
async addTask(taskName) {
72+
if (!this.url) {
73+
return;
74+
}
75+
await this.page.goto(this.url);
76+
await waitPageLoading(this.page);
77+
await this.page.locator('[data-bs-target="#insertTaskModal"]').click();
78+
const modal = this.page.locator('.modal.show');
79+
await modal.waitFor();
80+
await this.page.getByText('User tasks').click();
81+
const selector = modal.getByRole('combobox').first();
82+
await selector.click();
83+
const items = await this.page.getByRole('option').all();
84+
let testTaskItem = null;
85+
for (const item of items) {
86+
const itemText = await item.innerText();
87+
if (itemText.includes(taskName)) {
88+
testTaskItem = item;
89+
break;
90+
}
91+
}
92+
expect(testTaskItem).not.toBeNull();
93+
await /** @type {import('@playwright/test').Locator} */ (testTaskItem).click();
94+
await this.page.locator('#taskId').waitFor();
95+
await this.page.getByRole('button', { name: 'Insert' }).click();
96+
await waitModalClosed(this.page);
97+
}
5298
}
5399

54100
const workflowTest = baseTest.extend({
@@ -65,3 +111,5 @@ const workflowTest = baseTest.extend({
65111

66112
export const test = mergeTests(baseTest, workflowTest);
67113
export const expect = test.expect;
114+
115+
export async function addTaskToWorkflow() {}

0 commit comments

Comments
 (0)