Skip to content

Commit 046171e

Browse files
committed
Refactored SLURM account test to v2
1 parent 7ee1f40 commit 046171e

File tree

6 files changed

+161
-159
lines changed

6 files changed

+161
-159
lines changed

playwright.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ export default defineConfig({
1919
},
2020
dependencies: ['auth']
2121
},
22+
{
23+
name: 'collect_mock_tasks',
24+
testMatch: /v2\/collect_mock_tasks\.setup\.js/,
25+
use: {
26+
storageState: 'tests/.auth/user.json'
27+
},
28+
dependencies: ['auth']
29+
},
2230
{
2331
name: 'create_fake_task',
2432
testMatch: /v1\/create_fake_task\.setup\.js/,
@@ -33,7 +41,7 @@ export default defineConfig({
3341
...devices['Desktop Chrome'],
3442
storageState: 'tests/.auth/user.json'
3543
},
36-
dependencies: ['create_fake_task']
44+
dependencies: ['collect_mock_tasks', 'create_fake_task']
3745
},
3846
{
3947
name: 'firefox',

tests/v1/admin_jobs.spec.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,8 @@ test('Execute a job and show it on the job tables', async ({ page, request }) =>
2121
workflow2 = await createJob(page, request, 'input2', 'output2');
2222
});
2323

24-
await test.step('Open the admin area', async () => {
25-
await page.goto('/v1/admin');
26-
await waitPageLoading(page);
27-
await page.getByRole('link', { name: 'Admin area' }).click();
28-
await waitPageLoading(page);
29-
await page.getByRole('link', { name: 'Manage users' }).waitFor();
30-
});
31-
3224
await test.step('Open the admin jobs', async () => {
33-
await page.getByRole('link', { name: 'Jobs' }).nth(1).click();
25+
await page.goto('/v1/admin/jobs');
3426
await waitPageLoading(page);
3527
await page.getByRole('button', { name: 'Search jobs' }).waitFor();
3628
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitPageLoading } from '../utils.js';
3+
import path from 'path';
4+
import os from 'os';
5+
import fs from 'fs';
6+
7+
test('Collect mock tasks [v2]', async ({ page, request }) => {
8+
const tasksMockWheelUrl =
9+
'https://github.com/fractal-analytics-platform/fractal-server/raw/main/tests/v2/fractal_tasks_mock/dist/fractal_tasks_mock-0.0.1-py3-none-any.whl';
10+
11+
test.slow();
12+
13+
await test.step('Go to tasks page', async () => {
14+
await page.goto('/v2/tasks');
15+
await waitPageLoading(page);
16+
});
17+
18+
if ((await page.getByRole('table').last().getByText('MIP_compound').count()) > 0) {
19+
console.warn('WARNING: Mock tasks V2 already collected. Skipping tasks collection');
20+
return;
21+
}
22+
23+
/** @type {string} */
24+
let tasksMockWheelFile;
25+
26+
await test.step('Download fractal_tasks_mock wheel', async () => {
27+
const response = await request.get(tasksMockWheelUrl);
28+
expect(response.status()).toEqual(200);
29+
const body = await response.body();
30+
const tmpDir = path.resolve(os.tmpdir(), 'playwright');
31+
if (!fs.existsSync(tmpDir)) {
32+
fs.mkdirSync(tmpDir);
33+
}
34+
tasksMockWheelFile = path.resolve(tmpDir, 'fractal_tasks_mock-0.0.1-py3-none-any.whl');
35+
fs.writeFileSync(tasksMockWheelFile, body);
36+
});
37+
38+
await test.step('Collect mock tasks', async () => {
39+
await page.getByText('Local', { exact: true }).click();
40+
await page.getByRole('textbox', { name: 'Package', exact: true }).fill(tasksMockWheelFile);
41+
await page.getByRole('button', { name: 'Collect', exact: true }).click();
42+
43+
// Wait for Task collections table
44+
await page.waitForFunction(
45+
(expectedCount) => document.querySelectorAll('table').length === expectedCount,
46+
2
47+
);
48+
await expect(page.locator('table tbody tr:first-child td:nth-child(2)').first()).toContainText(
49+
'fractal_tasks_mock'
50+
);
51+
expect(await getStatus(page)).toMatch(/^(pending|installing)$/);
52+
});
53+
54+
await test.step('Wait tasks collection', async () => {
55+
await page.waitForFunction(
56+
() =>
57+
document.querySelector('table tbody tr:first-child td:nth-child(4)')?.textContent === 'OK'
58+
);
59+
});
60+
61+
await test.step('Check tasks list', async () => {
62+
await page.getByRole('table').last().getByRole('cell', { name: 'MIP_compound' }).waitFor();
63+
});
64+
65+
await test.step('Delete task collection log', async () => {
66+
const deleteCollectionLogBtn = page.locator(
67+
'table tr:first-child td:nth-child(5) button.btn-warning'
68+
);
69+
await deleteCollectionLogBtn.click();
70+
71+
// Confirm action modal
72+
const modalTitle = page.locator('.modal.show .modal-title');
73+
await modalTitle.waitFor();
74+
await expect(modalTitle).toHaveText('Confirm action');
75+
await expect(page.locator('.modal.show .modal-body')).toContainText(
76+
'Remove a task collection log'
77+
);
78+
79+
// Confirm the deletion
80+
await page.getByRole('button', { name: 'Confirm' }).click();
81+
});
82+
83+
await test.step('Cleanup temporary wheel file', async () => {
84+
fs.rmSync(tasksMockWheelFile);
85+
});
86+
});
87+
88+
/**
89+
* @param {import('@playwright/test').Page} page
90+
* @return {Promise<string>}
91+
*/
92+
async function getStatus(page) {
93+
const statusCell = page.locator('table tbody tr:first-child td:nth-child(4)').first();
94+
return await statusCell.innerText();
95+
}

tests/v2/run_mock_tasks.spec.js

Lines changed: 2 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { expect, test } from './workflow_fixture.js';
22
import { waitModalClosed, waitPageLoading } from '../utils.js';
3-
import path from 'path';
4-
import os from 'os';
53
import fs from 'fs';
64
import { createDataset } from './dataset_utils.js';
5+
import { waitTaskSubmitted, waitTasksSuccess } from './workflow_task_utils.js';
76

8-
test('Collect and run mock tasks [v2]', async ({ page, workflow, request }) => {
9-
const tasksMockWheelUrl =
10-
'https://github.com/fractal-analytics-platform/fractal-server/raw/main/tests/v2/fractal_tasks_mock/dist/fractal_tasks_mock-0.0.1-py3-none-any.whl';
11-
7+
test('Run mock tasks [v2]', async ({ page, workflow }) => {
128
await page.waitForURL(workflow.url);
139
await waitPageLoading(page);
1410

@@ -31,91 +27,6 @@ test('Collect and run mock tasks [v2]', async ({ page, workflow, request }) => {
3127
datasetName2 = name;
3228
});
3329

34-
await test.step('Go to tasks page', async () => {
35-
await page.goto('/v2/tasks');
36-
await waitPageLoading(page);
37-
});
38-
39-
let skipTasksCollection = false;
40-
if ((await page.getByRole('table').last().getByText('MIP_compound').count()) > 0) {
41-
console.warn('WARNING: Mock tasks V2 already collected. Skipping tasks collection');
42-
skipTasksCollection = true;
43-
}
44-
45-
if (!skipTasksCollection) {
46-
/** @type {string} */
47-
let tasksMockWheelFile;
48-
49-
await test.step('Download fractal_tasks_mock wheel', async () => {
50-
const response = await request.get(tasksMockWheelUrl);
51-
expect(response.status()).toEqual(200);
52-
const body = await response.body();
53-
const tmpDir = path.resolve(os.tmpdir(), 'playwright');
54-
if (!fs.existsSync(tmpDir)) {
55-
fs.mkdirSync(tmpDir);
56-
}
57-
tasksMockWheelFile = path.resolve(tmpDir, 'fractal_tasks_mock-0.0.1-py3-none-any.whl');
58-
fs.writeFileSync(tasksMockWheelFile, body);
59-
});
60-
61-
await test.step('Collect mock tasks', async () => {
62-
await page.getByText('Local', { exact: true }).click();
63-
await page.getByRole('textbox', { name: 'Package', exact: true }).fill(tasksMockWheelFile);
64-
await page.getByRole('button', { name: 'Collect', exact: true }).click();
65-
66-
if (await page.getByText('Already installed').isVisible()) {
67-
skipTasksCollection = true;
68-
return;
69-
}
70-
71-
// Wait for Task collections table
72-
await page.waitForFunction(
73-
(expectedCount) => document.querySelectorAll('table').length === expectedCount,
74-
2
75-
);
76-
await expect(
77-
page.locator('table tbody tr:first-child td:nth-child(2)').first()
78-
).toContainText('fractal_tasks_mock');
79-
expect(await getStatus(page)).toMatch(/^(pending|installing)$/);
80-
});
81-
82-
if (!skipTasksCollection) {
83-
await test.step('Wait tasks collection', async () => {
84-
await page.waitForFunction(
85-
() =>
86-
document.querySelector('table tbody tr:first-child td:nth-child(4)')?.textContent ===
87-
'OK'
88-
);
89-
});
90-
91-
await test.step('Check tasks list', async () => {
92-
await page.getByRole('table').last().getByRole('cell', { name: 'MIP_compound' }).waitFor();
93-
});
94-
95-
await test.step('Delete task collection log', async () => {
96-
const deleteCollectionLogBtn = page.locator(
97-
'table tr:first-child td:nth-child(5) button.btn-warning'
98-
);
99-
await deleteCollectionLogBtn.click();
100-
101-
// Confirm action modal
102-
const modalTitle = page.locator('.modal.show .modal-title');
103-
await modalTitle.waitFor();
104-
await expect(modalTitle).toHaveText('Confirm action');
105-
await expect(page.locator('.modal.show .modal-body')).toContainText(
106-
'Remove a task collection log'
107-
);
108-
109-
// Confirm the deletion
110-
await page.getByRole('button', { name: 'Confirm' }).click();
111-
});
112-
}
113-
114-
await test.step('Cleanup temporary wheel file', async () => {
115-
fs.rmSync(tasksMockWheelFile);
116-
});
117-
}
118-
11930
await test.step('Go to workflow page', async () => {
12031
await page.goto(workflow.url);
12132
await waitPageLoading(page);
@@ -400,15 +311,6 @@ test('Collect and run mock tasks [v2]', async ({ page, workflow, request }) => {
400311
});
401312
});
402313

403-
/**
404-
* @param {import('@playwright/test').Page} page
405-
* @return {Promise<string>}
406-
*/
407-
async function getStatus(page) {
408-
const statusCell = page.locator('table tbody tr:first-child td:nth-child(4)').first();
409-
return await statusCell.innerText();
410-
}
411-
412314
/**
413315
* @param {import('@playwright/test').Page} page
414316
* @param {import('@playwright/test').Locator} row
@@ -424,33 +326,3 @@ async function getJobId(page, row) {
424326
await page.locator('.modal.show .modal-header [aria-label="Close"]').click();
425327
return /** @type {RegExpMatchArray} */ (match)[1];
426328
}
427-
428-
/**
429-
* @param {import('@playwright/test').Page} page
430-
* @param {number} count
431-
*/
432-
async function waitTaskSubmitted(page, count) {
433-
await expect(page.getByText('The last job failed with the following error')).toHaveCount(0);
434-
if ((await page.locator('.job-status-icon.bi-check').count()) > 0) {
435-
return;
436-
}
437-
const spinners = page.locator('.job-status-submitted.spinner-border');
438-
await expect(spinners).toHaveCount(count);
439-
}
440-
441-
/**
442-
* @param {import('@playwright/test').Page} page
443-
* @param {number} count
444-
*/
445-
async function waitTasksSuccess(page, count) {
446-
const spinners = page.locator('.job-status-submitted.spinner-border');
447-
await expect(spinners).toHaveCount(0);
448-
const errorAlert = page.getByText('The last job failed with the following error');
449-
if (await errorAlert.isVisible()) {
450-
const error = await page.locator('.alert.border-danger').innerText();
451-
console.error(error);
452-
}
453-
await expect(errorAlert).toHaveCount(0);
454-
await page.locator('.job-status-icon.bi-check').first().waitFor();
455-
await expect(page.locator('.job-status-icon.bi-check')).toHaveCount(count);
456-
}
Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { waitPageLoading } from '../utils.js';
2+
import { createDataset } from './dataset_utils.js';
23
import { expect, test } from './workflow_fixture.js';
4+
import { waitTaskSubmitted, waitTasksSuccess } from './workflow_task_utils.js';
35

46
test('Add SLURM accounts for the admin and execute workflow using a specific account', async ({
57
page,
@@ -8,8 +10,12 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
810
await page.waitForURL(workflow.url);
911
await waitPageLoading(page);
1012

13+
await test.step('Create dataset', async () => {
14+
await createDataset(page, workflow.projectId);
15+
});
16+
1117
await test.step('Open the edit user page for admin user', async () => {
12-
await page.goto('/v1/admin/users/1/edit');
18+
await page.goto('/v2/admin/users/1/edit');
1319
await waitPageLoading(page);
1420
});
1521

@@ -22,7 +28,7 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
2228
.last()
2329
.fill(randomSlurmAccount);
2430
await page.getByRole('button', { name: 'Save' }).click();
25-
await page.waitForURL('/v1/admin/users');
31+
await page.waitForURL('/v2/admin/users');
2632
await waitPageLoading(page);
2733
});
2834

@@ -38,13 +44,11 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
3844
await page.getByText(randomSlurmAccount).waitFor();
3945
});
4046

41-
await test.step('Open workflow page', async () => {
47+
await test.step('Add task to workflow', async () => {
4248
await page.goto(workflow.url);
4349
await waitPageLoading(page);
44-
});
45-
46-
await test.step('Add task to workflow', async () => {
47-
await workflow.addFakeTask();
50+
await workflow.addCollectedTask('generic_task');
51+
await workflow.selectTask('generic_task');
4852
});
4953

5054
await test.step('Open Run workflow modal', async () => {
@@ -57,31 +61,31 @@ test('Add SLURM accounts for the admin and execute workflow using a specific acc
5761

5862
await test.step('Run workflow with new SLURM account', async () => {
5963
const modal = page.locator('.modal.show');
60-
await modal.getByRole('combobox', { name: 'Input dataset' }).selectOption('input');
61-
await modal.getByRole('combobox', { name: 'Output dataset' }).selectOption('output');
64+
await modal.getByRole('button', { name: 'Advanced Options' }).click();
6265
await modal.getByRole('combobox', { name: 'SLURM account' }).selectOption(randomSlurmAccount);
6366
const runBtn = page.locator('.modal.show').getByRole('button', { name: 'Run' });
6467
await runBtn.click();
6568
const confirmBtn = page.locator('.modal.show').getByRole('button', { name: 'Confirm' });
6669
await confirmBtn.click();
6770
});
6871

72+
await test.step('Wait task submitted', async () => {
73+
await waitTaskSubmitted(page, 1);
74+
});
75+
76+
await test.step('Wait for job completion', async () => {
77+
await waitTasksSuccess(page, 1);
78+
});
79+
6980
await test.step('Check SLURM account in workflow info modal', async () => {
70-
await page.waitForURL(
71-
`/v1/projects/${workflow.projectId}/workflows/${workflow.workflowId}/jobs`
72-
);
81+
await page.goto(`/v2/projects/${workflow.projectId}/workflows/${workflow.workflowId}/jobs`);
7382
await page.locator('table tbody').waitFor();
7483
await page.locator('table tbody tr').getByRole('button', { name: 'Info' }).click();
7584
const modalTitle = page.locator('.modal.show .modal-title');
7685
await modalTitle.waitFor();
7786
await expect(modalTitle).toContainText(`Workflow Job #`);
7887
const items = await page.locator('.modal.show').getByRole('listitem').allInnerTexts();
79-
expect(items[16]).toEqual('SLURM account');
80-
expect(items[17]).toEqual(randomSlurmAccount);
81-
});
82-
83-
await test.step('Wait for job completion', async () => {
84-
await workflow.triggerTaskSuccess();
85-
await page.getByRole('table').getByText('done', { exact: true }).waitFor();
88+
expect(items[items.length - 2]).toEqual('SLURM account');
89+
expect(items[items.length - 1]).toEqual(randomSlurmAccount);
8690
});
8791
});

0 commit comments

Comments
 (0)