Files
InvenTree/src/frontend/tests/pui_login.spec.ts
T
Oliver 7f1f2dbad2 [UI] Table column filters (#12103)
* Expose properties for column based filtering

* Adjust renderers

* Hide filter if name does not match

* Allow multiple filters on same column

* Better formatting

* Add filtering support for multiple tables

* Revert yarn.lock changes

* Fix date input props

* Updated column

* Add filter  to PartTable

* Add playwright tests for new column filters

* Update CHANGELOG

* Updated docs

* Reduce padding

* Update more table filters

* More filter columns

* Adjust playwright test

* Simplify playwright test

* Robustify playwright tests

* Add some delay

* Add some buffer time
2026-06-07 12:59:33 +10:00

159 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from './baseFixtures.js';
import { logoutUrl, noaccessuser } from './defaults.js';
import { navigate, openDetailAction } from './helpers.js';
import { doLogin } from './login.js';
import { TOTP } from 'otpauth';
/**
* Test various types of login failure
*/
test('Login - Failures', async ({ page }) => {
const loginWithError = async () => {
await page.getByRole('button', { name: 'Log In' }).click();
await page.getByText('Login failed', { exact: true }).waitFor();
await page.getByText('Check your input and try again').first().waitFor();
await page.reload();
};
// Navigate to the 'login' page
await navigate(page, logoutUrl);
await expect(page).toHaveTitle(/^InvenTree.*$/);
await page.waitForURL('**/web/login');
// Attempt login with invalid credentials
await page.getByLabel('login-username').fill('invalid user');
await page.getByLabel('login-password').fill('invalid password');
await loginWithError();
// Attempt login with valid (but disabled) user
await page.getByLabel('login-username').fill('ian');
await page.getByLabel('login-password').fill('inactive');
await loginWithError();
// Attempt login with no username
await page.getByLabel('login-username').fill('');
await page.getByLabel('login-password').fill('hunter2');
await loginWithError();
// Attempt login with no password
await page.getByLabel('login-username').fill('ian');
await page.getByLabel('login-password').fill('');
await loginWithError();
});
test('Login - Change Password', async ({ page }) => {
await doLogin(page, {
user: noaccessuser
});
// Navigate to the 'change password' page
await navigate(page, 'settings/user/account', { waitUntil: 'networkidle' });
await openDetailAction(page, 'account', 'change-password');
// First attempt with some errors
await page.getByLabel('password', { exact: true }).fill('youshallnotpass');
await page.getByLabel('input-password-1').fill('12345');
await page.getByLabel('input-password-2').fill('54321');
await page.getByRole('button', { name: 'Confirm' }).click();
await page.getByText('The two password fields didnt match').waitFor();
await page.getByLabel('input-password-2').clear();
await page.getByLabel('input-password-2').fill('12345');
await page.getByRole('button', { name: 'Confirm' }).click();
await page.getByText('This password is too short').waitFor();
await page.getByText('This password is entirely numeric').waitFor();
await page.waitForTimeout(250);
await page.getByLabel('password', { exact: true }).clear();
await page.getByLabel('input-password-1').clear();
await page.getByLabel('input-password-2').clear();
await page.getByLabel('password', { exact: true }).fill('youshallnotpass');
await page.getByLabel('input-password-1').fill('youshallnotpass');
await page.getByLabel('input-password-2').fill('youshallnotpass');
await page.getByRole('button', { name: 'Confirm' }).click();
await page.getByText('Password Changed').waitFor();
await page.getByText('The password was set successfully').waitFor();
});
// Tests for assigning MFA tokens to users
test('Login - MFA - TOTP', async ({ page }) => {
await doLogin(page, {
user: noaccessuser
});
await navigate(page, 'settings/user/security', { waitUntil: 'networkidle' });
// Expand the MFA section
await page
.getByRole('button', { name: 'Multi-Factor Authentication' })
.click();
await page.getByRole('button', { name: 'add-factor-totp' }).click();
// Ensure the user starts without any codes
await page
.getByText('No multi-factor tokens configured for this account')
.waitFor();
// Try to submit with an empty code
await page.getByRole('textbox', { name: 'text-input-otp-code' }).fill('');
await page.getByRole('button', { name: 'Submit', exact: true }).click();
await page.getByText('This field is required.').waitFor();
// Try to submit with an invalid secret
await page
.getByRole('textbox', { name: 'text-input-otp-code' })
.fill('ABCDEF');
await page.getByRole('button', { name: 'Submit', exact: true }).click();
await page.getByText('Incorrect code.').waitFor();
// Submit a valid code
const secret = await page
.getByLabel('otp-secret', { exact: true })
.innerText();
// Construct a TOTP code based on the secret
const totp = new TOTP({
secret: secret,
digits: 6,
period: 30
});
const token = totp.generate();
await page.getByRole('textbox', { name: 'text-input-otp-code' }).fill(token);
await page.getByRole('button', { name: 'Submit', exact: true }).click();
await page.getByText('TOTP token registered successfully').waitFor();
// View recovery codes
await page.getByRole('button', { name: 'view-recovery-codes' }).click();
await page
.getByText('The following one time recovery codes are available')
.waitFor();
await page.getByRole('button', { name: 'Close' }).click();
// Remove TOTP token
await page.getByRole('button', { name: 'remove-totp' }).click();
await page.getByRole('button', { name: 'Remove', exact: true }).click();
await page.getByText('TOTP token removed successfully').waitFor();
// And, once again there should be no configured tokens
await page
.getByText('No multi-factor tokens configured for this account')
.waitFor();
});