Skip to content

Commit be5283a

Browse files
committed
test: elevation
1 parent 96116db commit be5283a

5 files changed

Lines changed: 136 additions & 25 deletions

File tree

ui/src/client/ElevateClientDialog.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,16 @@ const ElevateClientDialog = observer(({clientName, clientId, fClose}: IProps) =>
4646
};
4747

4848
return (
49-
<Dialog open={true} onClose={handleClose} aria-labelledby="elevate-client-dialog-title">
50-
<DialogTitle id="elevate-client-dialog-title">Elevate Client: {clientName}</DialogTitle>
49+
<Dialog open={true} onClose={handleClose} className="elevate-client-dialog">
50+
<DialogTitle>Elevate Client: {clientName}</DialogTitle>
5151
<DialogContent>
5252
{needsElevation ? (
5353
<ElevationForm />
5454
) : (
5555
<FormControl fullWidth style={{marginTop: 8}}>
5656
<InputLabel id="elevate-duration-label">Duration</InputLabel>
5757
<Select
58+
className="elevate-duration"
5859
labelId="elevate-duration-label"
5960
label="Duration"
6061
value={durationSeconds}
@@ -69,9 +70,16 @@ const ElevateClientDialog = observer(({clientName, clientId, fClose}: IProps) =>
6970
)}
7071
</DialogContent>
7172
<DialogActions>
72-
<Button onClick={handleClose}>Cancel</Button>
73+
<Button className="elevate-cancel" onClick={handleClose}>
74+
Cancel
75+
</Button>
7376
{!needsElevation && (
74-
<Button onClick={handleConfirm} autoFocus color="primary" variant="contained">
77+
<Button
78+
className="elevate-confirm"
79+
onClick={handleConfirm}
80+
autoFocus
81+
color="primary"
82+
variant="contained">
7583
Elevate
7684
</Button>
7785
)}

ui/src/common/ElevationForm.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const ElevationForm = observer(() => {
3535
Complete sign-in in the new tab, then close it to continue.
3636
</Typography>
3737
<Button
38+
className="elevation-oidc-cancel"
3839
variant="outlined"
3940
fullWidth
4041
onClick={() => elevateStore.cleanupOidcElevate()}>
@@ -57,6 +58,7 @@ const ElevationForm = observer(() => {
5758
margin="dense"
5859
type="password"
5960
label="Password"
61+
className="elevation-password"
6062
value={password}
6163
onChange={(e) => {
6264
setPassword(e.target.value);
@@ -68,6 +70,7 @@ const ElevationForm = observer(() => {
6870
/>
6971
<Button
7072
type="submit"
73+
className="elevation-submit"
7174
disabled={password.length === 0}
7275
color="primary"
7376
variant="contained"
@@ -80,6 +83,7 @@ const ElevationForm = observer(() => {
8083
<>
8184
<Divider sx={{my: 2}}>or</Divider>
8285
<Button
86+
className="elevation-oidc"
8387
variant="contained"
8488
color="primary"
8589
fullWidth

ui/src/tests/client.test.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Page} from 'puppeteer';
22
import {newTest, GotifyTest} from './setup';
3-
import {count, innerText, waitForExists, waitToDisappear, clearField} from './utils';
3+
import {count, innerText, waitForExists, waitToDisappear, clearField, ClientCol} from './utils';
44
import {afterAll, beforeAll, describe, expect, it} from 'vitest';
55
import * as auth from './authentication';
66

@@ -15,26 +15,16 @@ beforeAll(async () => {
1515

1616
afterAll(async () => await gotify.close());
1717

18-
enum Col {
19-
Name = 1,
20-
Token = 2,
21-
LastSeen = 3,
22-
ElevationEnds = 4,
23-
Elevate = 5,
24-
Edit = 6,
25-
Delete = 7,
26-
}
27-
2818
const waitForClient =
2919
(name: string, row: number): (() => Promise<void>) =>
3020
async () => {
31-
await waitForExists(page, $table.cell(row, Col.Name), name);
21+
await waitForExists(page, $table.cell(row, ClientCol.Name), name);
3222
};
3323

3424
const updateClient =
3525
(id: number, data: {name?: string}): (() => Promise<void>) =>
3626
async () => {
37-
await page.click($table.cell(id, Col.Edit, '.edit'));
27+
await page.click($table.cell(id, ClientCol.Edit, '.edit'));
3828
await page.waitForSelector($dialog.selector());
3929
if (data.name) {
4030
const nameSelector = $dialog.input('.name');
@@ -79,21 +69,23 @@ describe('Client', () => {
7969

8070
expect(await count(page, $table.rows())).toBe(3);
8171

82-
expect(await innerText(page, $table.cell(1, Col.Name))).toContain('chrome');
83-
expect(await innerText(page, $table.cell(2, Col.Name))).toBe('phone');
84-
expect(await innerText(page, $table.cell(3, Col.Name))).toBe('desktop app');
72+
expect(await innerText(page, $table.cell(1, ClientCol.Name))).toContain('chrome');
73+
expect(await innerText(page, $table.cell(2, ClientCol.Name))).toBe('phone');
74+
expect(await innerText(page, $table.cell(3, ClientCol.Name))).toBe('desktop app');
8575
});
8676
it('updates client', updateClient(1, {name: 'firefox'}));
8777
it('has updated client name', waitForClient('firefox', 1));
8878
it('shows token', async () => {
89-
await page.click($table.cell(3, Col.Token, '.toggle-visibility'));
90-
expect((await innerText(page, $table.cell(3, Col.Token))).startsWith('C')).toBeTruthy();
79+
await page.click($table.cell(3, ClientCol.Token, '.toggle-visibility'));
80+
expect(
81+
(await innerText(page, $table.cell(3, ClientCol.Token))).startsWith('C')
82+
).toBeTruthy();
9183
});
9284
it('shows last seen', async () => {
93-
expect(await innerText(page, $table.cell(3, Col.LastSeen))).toBeTruthy();
85+
expect(await innerText(page, $table.cell(3, ClientCol.LastSeen))).toBeTruthy();
9486
});
9587
it('deletes client', async () => {
96-
await page.click($table.cell(2, Col.Delete, '.delete'));
88+
await page.click($table.cell(2, ClientCol.Delete, '.delete'));
9789

9890
await page.waitForSelector(selector.$confirmDialog.selector());
9991
await page.click(selector.$confirmDialog.button('.confirm'));
@@ -104,7 +96,7 @@ describe('Client', () => {
10496
expect(await count(page, $table.rows())).toBe(2);
10597
});
10698
it('deletes own client', async () => {
107-
await page.click($table.cell(1, Col.Delete, '.delete'));
99+
await page.click($table.cell(1, ClientCol.Delete, '.delete'));
108100

109101
// confirm delete
110102
await page.waitForSelector(selector.$confirmDialog.selector());

ui/src/tests/elevation.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import {Page} from 'puppeteer';
2+
import {newTest, GotifyTest} from './setup';
3+
import {clickByText, ClientCol, count, waitForExists, waitToDisappear} from './utils';
4+
import {afterAll, beforeAll, describe, expect, it} from 'vitest';
5+
import * as auth from './authentication';
6+
import * as selector from './selector';
7+
8+
let page: Page;
9+
let gotify: GotifyTest;
10+
beforeAll(async () => {
11+
gotify = await newTest();
12+
page = gotify.page;
13+
});
14+
15+
afterAll(async () => await gotify.close());
16+
17+
const $clientTable = selector.table('#client-table');
18+
const $clientDialog = selector.form('#client-dialog');
19+
20+
// This expects the session to be already elevated.
21+
const cancelElevationViaUI = async (row: number) => {
22+
await page.goto(gotify.url + '/#/clients');
23+
await waitForExists(page, selector.heading(), 'Clients');
24+
25+
await page.click($clientTable.cell(row, ClientCol.Elevate, '.elevate'));
26+
await page.waitForSelector('.elevate-client-dialog');
27+
28+
await page.click('.elevate-client-dialog .elevate-duration [role=combobox]');
29+
await clickByText(page, '[role="option"]', 'Cancel elevation');
30+
await waitToDisappear(page, '[role="listbox"]');
31+
32+
await page.click('.elevate-client-dialog .elevate-confirm');
33+
await waitToDisappear(page, '.elevate-client-dialog');
34+
};
35+
36+
const elevateViaForm = async (password: string) => {
37+
const passwordInput = '.elevation-password input';
38+
await page.waitForSelector(passwordInput);
39+
await page.type(passwordInput, password);
40+
await page.click('.elevation-submit');
41+
};
42+
43+
describe('Elevation', () => {
44+
it('does login', async () => await auth.login(page));
45+
46+
describe('setup', () => {
47+
it('navigates to clients', async () => {
48+
await page.click('#navigate-clients');
49+
await waitForExists(page, selector.heading(), 'Clients');
50+
});
51+
it('creates a test client', async () => {
52+
await page.click('#create-client');
53+
await page.waitForSelector($clientDialog.selector());
54+
await page.type($clientDialog.input('.name'), 'test-client');
55+
await page.click($clientDialog.button('.create'));
56+
await waitToDisappear(page, $clientDialog.selector());
57+
await page.waitForSelector($clientTable.row(2));
58+
expect(await count(page, $clientTable.rows())).toBe(2);
59+
});
60+
});
61+
62+
describe('Users page requires elevation', () => {
63+
it('de-elevates the current client via UI', () => cancelElevationViaUI(1));
64+
it('navigates to users and sees elevation form', async () => {
65+
await page.goto(gotify.url + '/#/users');
66+
await waitForExists(page, selector.heading(), 'Authentication Required');
67+
await page.waitForSelector('.elevation-password input');
68+
});
69+
it('elevates via password and sees users page', async () => {
70+
await elevateViaForm('admin');
71+
await waitForExists(page, selector.heading(), 'Users');
72+
expect(page.url()).toContain('/users');
73+
});
74+
});
75+
76+
describe('Client delete requires elevation', () => {
77+
it('de-elevates the current client via UI', () => cancelElevationViaUI(1));
78+
it('navigates to clients', async () => {
79+
await page.goto(gotify.url + '/#/clients');
80+
await waitForExists(page, selector.heading(), 'Clients');
81+
});
82+
it('clicks delete and sees elevation form in dialog', async () => {
83+
await page.click($clientTable.cell(2, ClientCol.Delete, '.delete'));
84+
await page.waitForSelector(selector.$confirmDialog.selector());
85+
await page.waitForSelector('.confirm-dialog .elevation-password input');
86+
});
87+
it('elevates', () => elevateViaForm('admin'));
88+
it('confirms deletion', async () => {
89+
await page.waitForSelector(selector.$confirmDialog.button('.confirm'));
90+
await page.click(selector.$confirmDialog.button('.confirm'));
91+
});
92+
it('has deleted the client', async () => {
93+
await waitToDisappear(page, $clientTable.row(2));
94+
expect(await count(page, $clientTable.rows())).toBe(1);
95+
});
96+
});
97+
});

ui/src/tests/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,13 @@ export const clearField = async (element: ElementHandle | Page, selector: string
6767
await elementHandle.click({clickCount: 3});
6868
await elementHandle.press('Backspace');
6969
};
70+
71+
export enum ClientCol {
72+
Name = 1,
73+
Token = 2,
74+
LastSeen = 3,
75+
ElevationEnds = 4,
76+
Elevate = 5,
77+
Edit = 6,
78+
Delete = 7,
79+
}

0 commit comments

Comments
 (0)