181 lines
6.3 KiB
TypeScript
181 lines
6.3 KiB
TypeScript
import type { Page } from '@playwright/test';
|
|
import { expect } from '@playwright/test';
|
|
import { createApi } from '../api.js';
|
|
import { test } from '../baseFixtures.js';
|
|
import { allaccessuser } from '../defaults.js';
|
|
import { doCachedLogin } from '../login.js';
|
|
import { setPluginState } from '../settings.js';
|
|
|
|
const resetDashboard = async (page: Page) => {
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Clear Widgets' }).click();
|
|
};
|
|
|
|
test('Dashboard - Basic', async ({ browser }) => {
|
|
const page = await doCachedLogin(browser);
|
|
|
|
// Reset dashboard widgets
|
|
await resetDashboard(page);
|
|
|
|
await page.getByText('Use the menu to add widgets').waitFor();
|
|
|
|
// Let's add some widgets
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Add Widget' }).click();
|
|
await page.getByLabel('dashboard-widgets-filter-input').fill('overdue order');
|
|
|
|
await page.getByLabel('add-widget-ovr-so').click();
|
|
await page.getByLabel('add-widget-ovr-po').click();
|
|
|
|
await page.getByLabel('dashboard-widgets-filter-clear').click();
|
|
|
|
// Close the widget
|
|
await page.getByRole('banner').getByRole('button').click();
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check that the widgets are visible
|
|
await page.getByText('Overdue Sales Orders').waitFor();
|
|
await page.getByText('Overdue Purchase Orders').waitFor();
|
|
|
|
// Let's remove one of the widgets
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Remove Widgets' }).click();
|
|
await page.getByLabel('remove-dashboard-item-ovr-so').click();
|
|
|
|
// Accept the layout
|
|
await page.getByLabel('dashboard-accept-layout').click();
|
|
});
|
|
|
|
test('Dashboard - Stocktake', async ({ browser }) => {
|
|
// Trigger a "stocktake" report from the dashboard
|
|
|
|
const page = await doCachedLogin(browser);
|
|
|
|
// Reset dashboard widgets
|
|
await resetDashboard(page);
|
|
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Add Widget' }).click();
|
|
await page.getByLabel('dashboard-widgets-filter-input').fill('stocktake');
|
|
await page.getByRole('button', { name: 'add-widget-stk' }).click();
|
|
|
|
await page.waitForTimeout(100);
|
|
await page.keyboard.press('Escape');
|
|
|
|
await page.getByRole('button', { name: 'Generate Stocktake Report' }).click();
|
|
|
|
await page.getByText('Select a category to include').waitFor();
|
|
await page.getByRole('button', { name: 'Generate', exact: true }).waitFor();
|
|
});
|
|
|
|
test('Dashboard - Plugins', async ({ browser }) => {
|
|
// Ensure that the "SampleUI" plugin is enabled
|
|
await setPluginState({
|
|
plugin: 'sampleui',
|
|
state: true
|
|
});
|
|
|
|
const page = await doCachedLogin(browser);
|
|
|
|
await resetDashboard(page);
|
|
|
|
// Add a dashboard widget from the SampleUI plugin
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Add Widget' }).click();
|
|
await page.getByLabel('dashboard-widgets-filter-input').fill('sample');
|
|
|
|
// Add the widget
|
|
await page.getByLabel(/add-widget-p-sampleui-sample-/).click();
|
|
|
|
// Close the widget
|
|
await page.getByRole('banner').getByRole('button').click();
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check that the widget is visible
|
|
await page.getByRole('heading', { name: 'Sample Dashboard Item' }).waitFor();
|
|
await page.getByText('Hello world! This is a sample').waitFor();
|
|
});
|
|
|
|
test('Dashboard - Preserve widget sizes', async ({ browser }) => {
|
|
// Regression: addWidget previously snapped every existing widget back to
|
|
// its minW/minH. Fix is in DashboardLayout.tsx::addWidget (overrideSize=false).
|
|
const TARGET_W = 10;
|
|
const TARGET_H = 6;
|
|
|
|
const readLayouts = (page: Page) =>
|
|
page.evaluate(() => {
|
|
const raw = localStorage.getItem('session-settings');
|
|
return raw ? (JSON.parse(raw)?.state?.layouts ?? {}) : {};
|
|
});
|
|
|
|
const user = allaccessuser;
|
|
|
|
const page = await doCachedLogin(browser, {
|
|
user: user
|
|
});
|
|
await resetDashboard(page);
|
|
|
|
// Add widget A; this also persists to the backend user profile.
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Add Widget' }).click();
|
|
await page.getByLabel('dashboard-widgets-filter-input').fill('overdue order');
|
|
await page.getByLabel('add-widget-ovr-so').click();
|
|
await page.getByRole('banner').getByRole('button').click();
|
|
await page.getByText('Overdue Sales Orders').waitFor();
|
|
await page.waitForTimeout(100);
|
|
|
|
// Inflate widget A on the backend profile and reload. The auth flow on
|
|
// page load rehydrates layouts from the profile, not localStorage, so a
|
|
// localStorage-only edit would be wiped out on reload.
|
|
const current = await readLayouts(page);
|
|
const inflated: Record<string, any[]> = {};
|
|
for (const bp of Object.keys(current)) {
|
|
inflated[bp] = current[bp].map((it: any) =>
|
|
it?.i === 'ovr-so' ? { ...it, w: TARGET_W, h: TARGET_H } : it
|
|
);
|
|
}
|
|
|
|
const api = createApi({
|
|
username: user.username,
|
|
password: user.testcred
|
|
});
|
|
|
|
(await api).patch('user/me/profile/', {
|
|
data: {
|
|
widgets: {
|
|
widgets: ['ovr-so'],
|
|
layouts: inflated
|
|
}
|
|
}
|
|
});
|
|
|
|
await page.reload();
|
|
await page.getByText('Overdue Sales Orders').waitFor();
|
|
await page.waitForTimeout(100);
|
|
|
|
// Sanity: profile rehydration produced the inflated values.
|
|
for (const [bp, items] of Object.entries(await readLayouts(page))) {
|
|
const entry = (items as any[]).find((i) => i?.i === 'ovr-so');
|
|
|
|
expect(entry?.w, `${bp}: ovr-so missing or wrong w`).toBe(TARGET_W);
|
|
expect(entry?.h, `${bp}: ovr-so missing or wrong h`).toBe(TARGET_H);
|
|
}
|
|
|
|
// Add widget B. With the bug, this clobbered widget A back to minW/minH.
|
|
await page.getByLabel('dashboard-menu').click();
|
|
await page.getByRole('menuitem', { name: 'Add Widget' }).click();
|
|
await page.getByLabel('dashboard-widgets-filter-input').fill('overdue order');
|
|
await page.getByLabel('add-widget-ovr-po').click();
|
|
await page.getByRole('banner').getByRole('button').click();
|
|
await page.getByText('Overdue Purchase Orders').waitFor();
|
|
await page.waitForTimeout(100);
|
|
|
|
for (const [bp, items] of Object.entries(await readLayouts(page))) {
|
|
const entry = (items as any[]).find((i) => i?.i === 'ovr-so');
|
|
expect(entry?.w, `${bp}: ovr-so width was reset`).toBe(TARGET_W);
|
|
expect(entry?.h, `${bp}: ovr-so height was reset`).toBe(TARGET_H);
|
|
}
|
|
});
|