2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-21 00:17:39 +00:00
Files
InvenTree/src/frontend/tests/pui_plugins.spec.ts
Lukas Wolf de270a5fe7 Supplier Mixin (#9761)
* commit initial draft for supplier import

* complete import wizard

* allow importing only mp and sp

* improved sample supplier plugin

* add docs

* add tests

* bump api version

* fix schema docu

* fix issues from code review

* commit unstaged changes

* fix test

* refactor part parameter bulk creation

* try to fix test

* fix tests

* fix test for mysql

* fix test

* support multiple suppliers by a single plugin

* hide import button if there is no supplier import plugin

* make form submitable via enter

* add pui test

* try to prevent race condition

* refactor api calls in pui tests

* try to fix tests again?

* fix tests

* trigger: ci

* update changelog

* fix api_version

* fix style

* Update CHANGELOG.md

Co-authored-by: Matthias Mair <code@mjmair.com>

* add user docs

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-10-18 07:13:03 +11:00

260 lines
8.0 KiB
TypeScript

import test from 'playwright/test';
import {
clearTableFilters,
clickOnRowMenu,
loadTab,
navigate,
setTableChoiceFilter
} from './helpers.js';
import { doCachedLogin } from './login.js';
import { setPluginState, setSettingState } from './settings.js';
// Unit test for plugin settings
test('Plugins - Settings', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'admin',
password: 'inventree'
});
// Ensure that the SampleIntegration plugin is enabled
await setPluginState({
plugin: 'sample',
state: true
});
// Navigate and select the plugin
await navigate(page, 'settings/admin/plugin/');
await clearTableFilters(page);
await page.getByLabel('table-search-input').fill('integration');
await page.waitForTimeout(250);
await page.waitForLoadState('networkidle');
await page
.getByRole('row', { name: 'SampleIntegrationPlugin' })
.getByRole('paragraph')
.click();
await page.getByRole('button', { name: 'Plugin Information' }).click();
await page
.getByLabel('Plugin Detail -')
.getByRole('button', { name: 'Plugin Settings' })
.waitFor();
// Edit numerical value
await page.getByLabel('edit-setting-NUMERICAL_SETTING').click();
const originalValue = await page
.getByLabel('number-field-value')
.inputValue();
await page
.getByLabel('number-field-value')
.fill(originalValue == '999' ? '1000' : '999');
await page.getByRole('button', { name: 'Submit' }).click();
// Change it back
await page.getByLabel('edit-setting-NUMERICAL_SETTING').click();
await page.getByLabel('number-field-value').fill(originalValue);
await page.getByRole('button', { name: 'Submit' }).click();
// Select supplier
await page.getByLabel('edit-setting-SELECT_COMPANY').click();
await page.getByLabel('related-field-value').fill('mouser');
await page.getByText('Mouser Electronics').click();
});
test('Plugins - User Settings', async ({ browser }) => {
const page = await doCachedLogin(browser);
// Ensure that the SampleIntegration plugin is enabled
await setPluginState({
plugin: 'sample',
state: true
});
// Navigate to user settings
await navigate(page, 'settings/user/');
await loadTab(page, 'Plugin Settings');
// User settings for the "Sample Plugin" should be visible
await page.getByRole('button', { name: 'Sample Plugin' }).click();
await page.getByText('User Setting 1').waitFor();
await page.getByText('User Setting 2').waitFor();
await page.getByText('User Setting 3').waitFor();
// Check for expected setting options
await page.getByLabel('edit-setting-USER_SETTING_3').click();
const val = await page.getByLabel('choice-field-value').inputValue();
await page.getByLabel('choice-field-value').click();
await page.getByRole('option', { name: 'Choice X' }).waitFor();
await page.getByRole('option', { name: 'Choice Y' }).waitFor();
await page.getByRole('option', { name: 'Choice Z' }).waitFor();
// Change the value of USER_SETTING_3
await page
.getByRole('option', { name: val == 'Choice X' ? 'Choice Z' : 'Choice X' })
.click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Setting USER_SETTING_3 updated successfully').waitFor();
});
// Test base plugin functionality
test('Plugins - Functionality', async ({ browser }) => {
// Navigate and select the plugin
const page = await doCachedLogin(browser, {
username: 'admin',
password: 'inventree',
url: 'settings/admin/plugin/'
});
// Filter plugins first
await clearTableFilters(page);
await setTableChoiceFilter(page, 'Sample', 'Yes');
await setTableChoiceFilter(page, 'Builtin', 'No');
// Activate the plugin
const cell = await page.getByText('Sample API Caller', { exact: true });
await clickOnRowMenu(cell);
// Activate the plugin (unless already activated)
if ((await page.getByRole('menuitem', { name: 'Deactivate' }).count()) == 0) {
await page.getByRole('menuitem', { name: 'Activate' }).click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('The plugin was activated').waitFor();
await page.waitForTimeout(250);
}
// Deactivate the plugin again
await clickOnRowMenu(cell);
await page.getByRole('menuitem', { name: 'Deactivate' }).click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('The plugin was deactivated').waitFor();
// Check for custom "mandatory" plugin
await clearTableFilters(page);
await setTableChoiceFilter(page, 'Mandatory', 'Yes');
await setTableChoiceFilter(page, 'Sample', 'Yes');
await setTableChoiceFilter(page, 'Builtin', 'No');
await page.getByText('1 - 1 / 1').waitFor();
await page
.getByRole('cell', { name: 'SampleLocatePlugin' })
.first()
.waitFor();
});
test('Plugins - Panels', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'admin',
password: 'inventree'
});
// Ensure that UI plugins are enabled
await setSettingState({
setting: 'ENABLE_PLUGINS_INTERFACE',
value: true
});
// Ensure that the SampleUI plugin is enabled
await setPluginState({
plugin: 'sampleui',
state: true
});
// Navigate to the "part" page
await navigate(page, 'part/69/');
// Ensure basic part tab is available
await loadTab(page, 'Part Details');
// Allow time for the plugin panels to load (they are loaded asynchronously)
await page.waitForLoadState('networkidle');
// Check out each of the plugin panels
await loadTab(page, 'Broken Panel');
await page.getByText('Error occurred while loading plugin content').waitFor();
await loadTab(page, 'Dynamic Panel');
await page.getByText('Instance ID: 69');
await page
.getByText('This panel has been dynamically rendered by the plugin system')
.waitFor();
await loadTab(page, 'Part Panel');
await page.getByText('This content has been rendered by a custom plugin');
// Disable the plugin, and ensure it is no longer visible
await setPluginState({
plugin: 'sampleui',
state: false
});
});
/**
* Unit test for custom admin integration for plugins
*/
test('Plugins - Custom Admin', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'admin',
password: 'inventree'
});
// Ensure that the SampleUI plugin is enabled
await setPluginState({
plugin: 'sampleui',
state: true
});
// Navigate to the "admin" page
await navigate(page, 'settings/admin/plugin/');
// Open the plugin drawer, and ensure that the custom admin elements are visible
await page.getByText('SampleUI').click();
await page.getByRole('button', { name: 'Plugin Information' }).click();
await page
.getByLabel('Plugin Detail')
.getByRole('button', { name: 'Plugin Settings' })
.click();
await page.getByRole('button', { name: 'Plugin Configuration' }).click();
// Check for expected custom elements
await page
.getByRole('heading', { name: 'Custom Plugin Configuration Content' })
.waitFor();
await page.getByText('apple: banana').waitFor();
await page.getByText('foo: bar').waitFor();
await page.getByText('hello: world').waitFor();
});
test('Plugins - Locate Item', async ({ browser }) => {
const page = await doCachedLogin(browser, {
username: 'admin',
password: 'inventree'
});
// Ensure that the sample location plugin is enabled
await setPluginState({
plugin: 'samplelocate',
state: true
});
// Navigate to the "stock item" page
await navigate(page, 'stock/item/287/');
await page.waitForLoadState('networkidle');
// "Locate" this item
await page.getByLabel('action-button-locate-item').click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Item location requested').waitFor();
// Show the location
await page.getByLabel('breadcrumb-1-factory').click();
await page.getByLabel('action-button-locate-item').click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Item location requested').waitFor();
});