Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 41 additions & 14 deletions ui/e2e/auth-flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ test.describe('auth flows', () => {
await loginViaUI(page, username, oldPassword);

// Open user menu and start password change
await page.locator('aside').getByRole('button').filter({ hasText: username }).click();
await page
.locator('aside')
.getByRole('button')
.filter({ hasText: username })
.click();
await page.getByRole('menuitem', { name: 'Change Password' }).click();

const dialog = page.getByRole('dialog');
Expand All @@ -52,7 +56,9 @@ test.describe('auth flows', () => {
await dialog.getByLabel('Confirm New Password').fill(newPassword);
await dialog.getByRole('button', { name: 'Change Password' }).click();

await expect(dialog.getByText('Password changed successfully')).toBeVisible();
await expect(
dialog.getByText('Password changed successfully')
).toBeVisible();

// Clear session and login with new password
await clearSession(page);
Expand All @@ -66,7 +72,10 @@ test.describe('auth flows', () => {
expect(failResponse.status()).toBe(401);
});

test('manager can create DAGs but cannot manage users', async ({ page, request }) => {
test('manager can create DAGs but cannot manage users', async ({
page,
request,
}) => {
const stack = await loadStack();
const adminToken = await loginViaAPI(
request,
Expand Down Expand Up @@ -94,7 +103,9 @@ test.describe('auth flows', () => {
await expect(dialog).toBeVisible();
await dialog.getByLabel('Workflow name').fill(dagName);
await dialog.getByRole('button', { name: 'Create' }).click();
await expect(page).toHaveURL(new RegExp(`/dags/${dagName}/spec$`));
await expect
.poll(() => new URL(page.url()).pathname)
.toMatch(new RegExp(`^/dags/${dagName}(/spec)?$`));
await expect(
page.getByRole('heading', { level: 1, name: dagName, exact: true })
).toBeVisible();
Expand All @@ -104,7 +115,10 @@ test.describe('auth flows', () => {
await expect(page).not.toHaveURL(/\/users$/);
});

test('operator cannot write DAGs but can execute them', async ({ page, request }) => {
test('operator cannot write DAGs but can execute them', async ({
page,
request,
}) => {
const stack = await loadStack();
const adminToken = await loginViaAPI(
request,
Expand Down Expand Up @@ -137,22 +151,32 @@ steps:
// Operator should not see Create button
await useDefaultWorkspaceScope(page);
await page.goto('/dags/');
await expect(page.getByRole('button', { name: 'Create new DAG' })).toBeHidden();
await expect(
page.getByRole('button', { name: 'Create new DAG' })
).toBeHidden();

// Operator should not see Rename/Delete on spec tab
await page.goto(`/dags/${encodeURIComponent(fileName)}/spec`);
// Operator should not see Rename/Delete in the workflow bar
await page.goto(`/dags/${encodeURIComponent(fileName)}`);
await expect(
page.getByRole('heading', { level: 1, name: dagName, exact: true })
).toBeVisible();
const bar = page.getByTestId('ww-bar');
await expect(
page.getByRole('button', { name: 'Rename', exact: true })
).toBeHidden();
bar.getByRole('button', { name: 'Refresh', exact: true })
).toBeVisible();
await expect(
page.getByRole('button', { name: 'Delete', exact: true })
).toBeHidden();
bar.getByRole('button', { name: 'Rename', exact: true })
).toHaveCount(0);
await expect(
bar.getByRole('button', { name: 'Delete', exact: true })
).toHaveCount(0);

// Operator can still execute via API
const operatorToken = await loginViaAPI(request, operatorUser, operatorPass);
const operatorToken = await loginViaAPI(
request,
operatorUser,
operatorPass
);
const startResponse = await request.post(
`/api/v1/dags/${encodeURIComponent(fileName)}/start?remoteNode=local`,
{
Expand All @@ -166,7 +190,10 @@ steps:
expect(startResponse.ok()).toBeTruthy();
});

test('admin resets user password from management page', async ({ page, request }) => {
test('admin resets user password from management page', async ({
page,
request,
}) => {
const stack = await loadStack();
const adminToken = await loginViaAPI(
request,
Expand Down
35 changes: 35 additions & 0 deletions ui/e2e/fixtures/dags/visual-http-jq.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Demo: fetch JSON over HTTP, filter it with jq, log the result.
description: Fetch a JSON document, extract its title with jq and log it.
steps:
- id: fetch
name: fetch
action: http.request
with:
method: GET
url: https://httpbin.org/json
format: json
silent: true
output:
body:
from: stdout
decode: json
select: .body

- id: title
name: extract title
depends: [fetch]
action: jq.filter
with:
filter: .slideshow.title
data: ${steps.fetch.outputs.body}
raw: true
output:
text:
from: stdout
decode: text # plain text, no JSON quotes

- name: show title
depends: [title]
action: log.write
with:
message: "Title: ${steps.title.outputs.text}"
23 changes: 23 additions & 0 deletions ui/e2e/fixtures/dags/visual-params.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Demo: params, consts, declared outputs and continue_on.
description: Use params and consts, tolerate one failing step, then read a param and an output.
params:
- GREETING: hello
- TARGET: world
consts:
- SUFFIX: "!"
steps:
- id: prepare
name: prepare
run: printf 'token=abc123\n' >> "$DAGU_OUTPUT_FILE"
outputs:
- name: token

- name: flaky
depends: [prepare]
run: exit 1
continue_on:
failure: true

- name: report
depends: [flaky]
run: echo "${params.GREETING} ${params.TARGET}${consts.SUFFIX} token=${steps.prepare.outputs.token}"
32 changes: 32 additions & 0 deletions ui/e2e/fixtures/dags/visual-router.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Demo: route on a value to one of two handler steps.
description: Produce a status code, route it to a handler, and run lifecycle handlers.
type: graph
steps:
- id: produce
name: produce code
run: echo 200
output:
code:
from: stdout
decode: text

- name: route code
depends: [produce]
action: router.route
with:
value: "${steps.produce.outputs.code}"
routes:
"re:^2\\d\\d$": [handle_ok]
"re:^[^2]\\d\\d$": [handle_other]

- name: handle_ok
run: echo "ok branch"

- name: handle_other
run: echo "other branch"

handler_on:
success:
run: echo "dag succeeded"
failure:
run: echo "dag failed"
Loading