[PUI] Add playground / pricing tests (#7057)

* Only render categories in overview if there is data
Red #7025

* add option to disable accordions

* remove unneeded log

* make optional

* add disabled state to panels

* add missing panels to overview

* use enum for refs

* add quickjump anchors

* use navigation function instaed

* make links more distinguishable

* fix type

* format ticks using currency

* add tooltip formatter

* fix type

* add pricing tests

* move test

* add more playground tests

* use plattform for logins

* add id for chart for easier testing

* test hover state and bom pricing details

* test linked rows works

* disable flaky test

* cleanup tests

* adjust test assumptions to dataset

* fix test

* remove second test stage

* remove unnecessary step

* open up tab instaed of checking for it

* add test for Dashboard

* add Supplier Pricing test

* add internal pricing test

* added variant pricing test

* add test for Purchase History

* make sure button is enabled

* remove submit

* remove timeout
going back to default 30s

* make less ambigious

* reintroduce higher timeout

* change url back

* Revert "change url back"

This reverts commit 9d20d2a86e8fd93df5e5f006617b32a95f578fc4.

* fix test

* just use cancel

* revert url change

* remove timeput

* use PUI URL - baseUrl is pointing to CUI

* fix url for testin

* reintroduce options

* use default url

* disable non working test for now

* run in debug mode

* use PUI for testing to actually get coverage
This commit is contained in:
Matthias Mair 2024-04-23 00:24:34 +02:00 committed by GitHub
parent 2e0b197457
commit 86597ce717
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 269 additions and 10 deletions

View File

@ -36,6 +36,9 @@ export default defineConfig({
},
{
command: 'invoke server -a 127.0.0.1:8000',
env: {
INVENTREE_DEBUG: 'True'
},
url: 'http://127.0.0.1:8000/api/',
reuseExistingServer: !process.env.CI,
stdout: 'pipe',

View File

@ -37,7 +37,13 @@ import { InvenTreeTable } from '../../../tables/InvenTreeTable';
import { NoPricingData } from './PricingPanel';
// Display BOM data as a pie chart
function BomPieChart({ data, currency }: { data: any[]; currency: string }) {
function BomPieChart({
data,
currency
}: {
readonly data: any[];
readonly currency: string;
}) {
return (
<ResponsiveContainer width="100%" height={500}>
<PieChart>
@ -78,7 +84,13 @@ function BomPieChart({ data, currency }: { data: any[]; currency: string }) {
}
// Display BOM data as a bar chart
function BomBarChart({ data, currency }: { data: any[]; currency: string }) {
function BomBarChart({
data,
currency
}: {
readonly data: any[];
readonly currency: string;
}) {
return (
<ResponsiveContainer width="100%" height={500}>
<BarChart data={data}>
@ -113,8 +125,8 @@ export default function BomPricingPanel({
part,
pricing
}: {
part: any;
pricing: any;
readonly part: any;
readonly pricing: any;
}): ReactNode {
const table = useTable('pricing-bom');

View File

@ -189,7 +189,7 @@ export default function PricingOverviewPanel({
<DataTable records={overviewData} columns={columns} />
</Stack>
<ResponsiveContainer width="100%" height={500}>
<BarChart data={overviewData}>
<BarChart data={overviewData} id="pricing-overview-chart">
<XAxis dataKey="title" />
<YAxis
tickFormatter={(value, index) =>

View File

@ -43,9 +43,9 @@ export default function PricingPanel({
label: panelOptions;
title: string;
visible: boolean;
disabled?: boolean | undefined;
disabled?: boolean;
}): ReactNode {
const is_disabled = disabled === undefined ? false : disabled;
const is_disabled = disabled ?? false;
return (
visible && (
<Accordion.Item value={label} id={label}>

View File

@ -1,6 +1,6 @@
export const classicUrl = 'http://127.0.0.1:8000';
export const baseUrl = `${classicUrl}/platform`;
export const baseUrl = './platform';
export const loginUrl = `${baseUrl}/login`;
export const logoutUrl = `${baseUrl}/logout`;
export const homeUrl = `${baseUrl}/home`;

View File

@ -25,13 +25,15 @@ export const doLogin = async (page, username?: string, password?: string) => {
export const doQuickLogin = async (
page,
username?: string,
password?: string
password?: string,
url?: string
) => {
username = username ?? user.username;
password = password ?? user.password;
url = url ?? baseUrl;
// await page.goto(logoutUrl);
await page.goto(`${baseUrl}/login/?login=${username}&password=${password}`);
await page.goto(`${url}/login/?login=${username}&password=${password}`);
await page.waitForURL('**/platform/home');
await page.waitForTimeout(250);
};

View File

@ -0,0 +1,88 @@
import { test } from '../baseFixtures.js';
import { baseUrl } from '../defaults.js';
import { doQuickLogin } from '../login.js';
const newPartName = 'UITESTIN123';
test('PUI - Pages - Index - Playground', async ({ page }) => {
await doQuickLogin(page);
await page.goto('./');
// Playground
await page.getByRole('tab', { name: 'Playground' }).click();
await page.getByRole('button', { name: 'API Forms' }).click();
// New Part
await page.getByRole('button', { name: 'Create New Part' }).click();
await page.locator('#react-select-3-input').fill('category 0');
await page
.getByRole('option', { name: 'Category 0' })
.locator('div')
.first()
.click();
await page.getByLabel('Name *').fill(newPartName);
await page.getByLabel('Initial Stock Quantity *').fill('1');
await page
.getByLabel('Create Part')
.getByRole('button', { name: 'Cancel' })
.click();
// Edit Part
await page.getByRole('button', { name: 'Edit Part' }).click();
await page.getByLabel('IPN').click();
await page.getByLabel('IPN').fill(newPartName);
await page
.getByLabel('Edit Part')
.getByRole('button', { name: 'Cancel' })
.click();
// Create Stock Item
await page.getByRole('button', { name: 'Create Stock Item' }).click();
await page.locator('#react-select-25-input').fill('R_1K_0402_1');
await page.getByText('R_1K_0402_1%').click();
await page
.getByLabel('Add Stock Item')
.getByRole('button', { name: 'Cancel' })
.click();
// EditCategory
await page.getByRole('button', { name: 'Edit Category' }).click();
await page.locator('.css-1xvbfjt-Input2').first().click();
await page.getByText('Category 0').click();
await page
.getByLabel('Edit Category')
.getByRole('button', { name: 'Cancel' })
.click();
// Create Attachment
await page.getByRole('button', { name: 'Create Attachment' }).click();
await page.getByLabel('Attachment *').waitFor();
await page.getByRole('button', { name: 'Cancel' }).click();
// TODO: actually create an attachment
// Create Part new Modal
await page.getByRole('button', { name: 'Create Part new Modal' }).click();
await page.locator('.css-1xvbfjt-Input2').first().click();
await page.getByText('Category 0').click();
await page
.getByLabel('Create part')
.getByRole('button', { name: 'Cancel' })
.click();
// Status Label
await page.getByRole('button', { name: 'Status labels' }).click();
await page.getByRole('textbox').dblclick();
await page.getByRole('textbox').fill('50');
await page.getByText('Attention needed').waitFor();
});
test('PUI - Pages - Index - Dashboard', async ({ page }) => {
await doQuickLogin(page);
// Dashboard auto update
await page.getByRole('tab', { name: 'Dashboard' }).click();
await page.getByText('Autoupdate').click();
await page.waitForTimeout(500);
await page.getByText('Autoupdate').click();
await page.getByText('This page is a replacement').waitFor();
});

View File

@ -0,0 +1,154 @@
import { test } from '@playwright/test';
import { baseUrl } from '../defaults';
import { doQuickLogin } from '../login';
test('PUI - Pages - Part - Pricing (Nothing, BOM)', async ({ page }) => {
await doQuickLogin(page);
// Part with no history
await page.goto(`${baseUrl}/part/82/pricing`);
await page.getByText('1551ABK').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Purchase History' }).isDisabled();
await page.getByRole('button', { name: 'Internal Pricing' }).isDisabled();
await page.getByRole('button', { name: 'Supplier Pricing' }).isDisabled();
// Part with history
await page.goto(`${baseUrl}/part/108/pricing`);
await page.getByText('Part: Blue Chair').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Internal Pricing' }).isDisabled();
await page.getByRole('button', { name: 'Sale History' }).isDisabled();
await page.getByRole('button', { name: 'Sale Pricing' }).isDisabled();
await page.getByRole('button', { name: 'BOM Pricing' }).isEnabled();
// Overview Graph
let graph = page.locator('#pricing-overview-chart');
await graph.waitFor();
await graph.getByText('$60').waitFor();
await graph.getByText('BOM Pricing').waitFor();
await graph.getByText('Overall Pricing').waitFor();
await graph.locator('path').nth(1).hover();
await page.getByText('min_value : $50').waitFor();
// BOM Pricing
await page.getByRole('button', { name: 'BOM Pricing' }).click();
await page.getByText('Bar Chart').click();
await page.getByText('total_price_min').waitFor();
await page.getByText('Pie Chart').click();
await page.getByRole('button', { name: 'Quantity Not sorted' }).waitFor();
await page.getByRole('button', { name: 'Unit Price Not sorted' }).waitFor();
// BOM Pricing - linkjumping
await page.getByText('Wood Screw').waitFor();
await page.getByText('Wood Screw').click();
await page.waitForURL('**/part/98/pricing');
});
test('PUI - Pages - Part - Pricing (Supplier)', async ({ page }) => {
await doQuickLogin(page);
// Part
await page.goto(`${baseUrl}/part/55/pricing`);
await page.getByText('Part: C_100nF_0603').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Purchase History' }).isEnabled();
await page.getByRole('button', { name: 'Internal Pricing' }).isDisabled();
await page.getByRole('button', { name: 'Supplier Pricing' }).isEnabled();
// Supplier Pricing
await page.getByRole('button', { name: 'Supplier Pricing' }).click();
await page.waitForTimeout(500);
await page.getByRole('button', { name: 'SKU Not sorted' }).waitFor();
// Supplier Pricing - linkjumping
let target = page.getByText('ARR-26041-LPC').first();
await target.waitFor();
await target.click();
// await page.waitForURL('**/purchasing/supplier-part/697/');
});
test('PUI - Pages - Part - Pricing (Variant)', async ({ page }) => {
await doQuickLogin(page);
// Part
await page.goto(`${baseUrl}/part/106/pricing`);
await page.getByText('Part: Chair').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Internal Pricing' }).isDisabled();
await page.getByRole('button', { name: 'BOM Pricing' }).isEnabled();
await page.getByRole('button', { name: 'Variant Pricing' }).isEnabled();
await page.getByRole('button', { name: 'Sale Pricing' }).isDisabled();
await page.getByRole('button', { name: 'Sale History' }).isDisabled();
// Variant Pricing
await page.getByRole('button', { name: 'Variant Pricing' }).click();
await page.waitForTimeout(500);
await page.getByRole('button', { name: 'Variant Part Not sorted' }).click();
// Variant Pricing - linkjumping
let target = page.getByText('Green Chair').first();
await target.waitFor();
await target.click();
await page.waitForURL('**/part/109/pricing');
});
test('PUI - Pages - Part - Pricing (Internal)', async ({ page }) => {
await doQuickLogin(page);
// Part
await page.goto(`${baseUrl}/part/65/pricing`);
await page.getByText('Part: M2x4 SHCS').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Purchase History' }).isDisabled();
await page.getByRole('button', { name: 'Internal Pricing' }).isEnabled();
await page.getByRole('button', { name: 'Supplier Pricing' }).isDisabled();
// Internal Pricing
await page.getByRole('button', { name: 'Internal Pricing' }).click();
await page.getByRole('button', { name: 'Price Break Not sorted' }).waitFor();
// Internal Pricing - editing
await page.getByRole('row', { name: '1 NZ$' }).getByRole('button').click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByText('Part *M2x4 SHCSSocket head').click();
await page.getByText('Part *M2x4 SHCSSocket head').click();
});
test('PUI - Pages - Part - Pricing (Purchase)', async ({ page }) => {
await doQuickLogin(page);
// Part
await page.goto(`${baseUrl}/part/69/pricing`);
await page.getByText('Part: 530470210').waitFor();
await page.getByRole('tab', { name: 'Part Pricing' }).click();
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
await page.getByRole('button', { name: 'Pricing Overview' }).waitFor();
await page.getByText('Last Updated').waitFor();
await page.getByRole('button', { name: 'Purchase History' }).isEnabled();
await page.getByRole('button', { name: 'Internal Pricing' }).isDisabled();
await page.getByRole('button', { name: 'Supplier Pricing' }).isDisabled();
// Purchase History
await page.getByRole('button', { name: 'Purchase History' }).click();
await page
.getByRole('button', { name: 'Purchase Order Not sorted' })
.waitFor();
await page.getByText('2022-04-29').waitFor();
});