mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[CI] Playwright improvements (#9395)
* Allow port 4173 (vite preview) * Change 'base' attr based on vite command * Allow api_host to be specified separately * Harden API host functionality * Adjust server selections * Cleanup vite.config.ts * Adjust playwright configuration - Allow to run in "production" mode - Builds the code first - Runs only the backend web server - Not suitable for coverage * Tweak github actions * Tweak QC file * Reduce number of steps * Tweak CI file * Fix typo * Ensure translation before build * Fix hard-coded test * Test tweaks * uncomment * Revert some changes * Run with gunicorn, single worker * Reduce log output in DEBUG mode * Update deps * Add global-setup func * Fix for .gitignore file * Cached auth state * Tweak login func * Updated tests * Enable parallel workers again * Simplify config * Try with a single worker again * Single retry mode * Run auth setup first - Prevent issues with parallel test doing login * Improve test setup process * Tweaks * Bump to 3 workers * Tweak playwright settings * Revert change * Revert change
This commit is contained in:
parent
858eb8f807
commit
7f5a447769
28
.github/workflows/qc_checks.yaml
vendored
28
.github/workflows/qc_checks.yaml
vendored
@ -554,7 +554,7 @@ jobs:
|
|||||||
chmod +rw /home/runner/work/InvenTree/db.sqlite3
|
chmod +rw /home/runner/work/InvenTree/db.sqlite3
|
||||||
invoke migrate
|
invoke migrate
|
||||||
|
|
||||||
platform_ui:
|
web_ui:
|
||||||
name: Tests - Web UI
|
name: Tests - Web UI
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
@ -584,7 +584,6 @@ jobs:
|
|||||||
INVENTREE_DB_PASSWORD: inventree_password
|
INVENTREE_DB_PASSWORD: inventree_password
|
||||||
INVENTREE_DEBUG: true
|
INVENTREE_DEBUG: true
|
||||||
INVENTREE_PLUGINS_ENABLED: false
|
INVENTREE_PLUGINS_ENABLED: false
|
||||||
VITE_COVERAGE: true
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||||
@ -599,13 +598,19 @@ jobs:
|
|||||||
apt-dependency: postgresql-client libpq-dev
|
apt-dependency: postgresql-client libpq-dev
|
||||||
pip-dependency: psycopg2
|
pip-dependency: psycopg2
|
||||||
- name: Set up test data
|
- name: Set up test data
|
||||||
run: invoke dev.setup-test -iv
|
run: |
|
||||||
- name: Rebuild thumbnails
|
invoke dev.setup-test -iv
|
||||||
run: invoke int.rebuild-thumbnails
|
invoke int.rebuild-thumbnails
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: invoke int.frontend-compile
|
run: |
|
||||||
- name: Install Playwright Browsers
|
invoke int.frontend-compile
|
||||||
run: cd src/frontend && npx playwright install --with-deps
|
cd src/frontend && npx playwright install --with-deps
|
||||||
|
- name: Set Production Mode
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
run: echo "VITE_PRODUCTION_BUILD=true" >> $GITHUB_ENV
|
||||||
|
- name: Set Coverage Mode
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
run: echo "VITE_COVERAGE_BUILD=true" >> $GITHUB_ENV
|
||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
id: tests
|
id: tests
|
||||||
run: cd src/frontend && npx nyc playwright test
|
run: cd src/frontend && npx nyc playwright test
|
||||||
@ -616,16 +621,17 @@ jobs:
|
|||||||
path: src/frontend/playwright-report/
|
path: src/frontend/playwright-report/
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
- name: Report coverage
|
- name: Report coverage
|
||||||
if: always()
|
if: github.event_name != 'pull_request'
|
||||||
run: cd src/frontend && npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --exclude-after-remap false
|
run: cd src/frontend && npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --exclude-after-remap false
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # pin@v5.4.0
|
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # pin@v5.4.0
|
||||||
if: always()
|
if: github.event_name != 'pull_request'
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
slug: inventree/InvenTree
|
slug: inventree/InvenTree
|
||||||
flags: web
|
flags: web
|
||||||
- name: Upload bundler info
|
- name: Upload bundler info
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
@ -633,7 +639,7 @@ jobs:
|
|||||||
yarn install
|
yarn install
|
||||||
yarn run build
|
yarn run build
|
||||||
|
|
||||||
platform_ui_build:
|
web_ui_build:
|
||||||
name: Build - Web UI
|
name: Build - Web UI
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
@ -406,13 +406,22 @@ def get_frontend_settings(debug=True):
|
|||||||
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
|
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the base URL
|
# Set the base URL for the user interface
|
||||||
|
# This is the UI path e.g. '/web/'
|
||||||
if 'base_url' not in frontend_settings:
|
if 'base_url' not in frontend_settings:
|
||||||
frontend_settings['base_url'] = (
|
frontend_settings['base_url'] = (
|
||||||
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
|
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
|
||||||
or 'web'
|
or 'web'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# If provided, specify the API host
|
||||||
|
api_host = frontend_settings.get('api_host', None) or get_setting(
|
||||||
|
'INVENTREE_FRONTEND_API_HOST', 'frontend_api_host', None
|
||||||
|
)
|
||||||
|
|
||||||
|
if api_host:
|
||||||
|
frontend_settings['api_host'] = api_host
|
||||||
|
|
||||||
# Set the server list
|
# Set the server list
|
||||||
frontend_settings['server_list'] = frontend_settings.get('server_list', [])
|
frontend_settings['server_list'] = frontend_settings.get('server_list', [])
|
||||||
|
|
||||||
|
@ -1088,6 +1088,7 @@ if DEBUG:
|
|||||||
'http://localhost',
|
'http://localhost',
|
||||||
'http://*.localhost',
|
'http://*.localhost',
|
||||||
'http://*localhost:8000',
|
'http://*localhost:8000',
|
||||||
|
'http://*localhost:4173',
|
||||||
'http://*localhost:5173',
|
'http://*localhost:5173',
|
||||||
]:
|
]:
|
||||||
if origin not in CSRF_TRUSTED_ORIGINS:
|
if origin not in CSRF_TRUSTED_ORIGINS:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
@ -60,13 +61,16 @@ def check_system_health(**kwargs):
|
|||||||
|
|
||||||
if not is_worker_running(**kwargs): # pragma: no cover
|
if not is_worker_running(**kwargs): # pragma: no cover
|
||||||
result = False
|
result = False
|
||||||
|
if not settings.DEBUG:
|
||||||
logger.warning('Background worker check failed')
|
logger.warning('Background worker check failed')
|
||||||
|
|
||||||
if not InvenTree.helpers_email.is_email_configured(): # pragma: no cover
|
if not InvenTree.helpers_email.is_email_configured(): # pragma: no cover
|
||||||
result = False
|
result = False
|
||||||
|
if not settings.DEBUG:
|
||||||
logger.warning('Email backend not configured')
|
logger.warning('Email backend not configured')
|
||||||
|
|
||||||
if not result: # pragma: no cover
|
if not result: # pragma: no cover
|
||||||
|
if not settings.DEBUG:
|
||||||
logger.warning('InvenTree system health checks failed')
|
logger.warning('InvenTree system health checks failed')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"@mantine/notifications": "^7.16.0",
|
"@mantine/notifications": "^7.16.0",
|
||||||
"@mantine/spotlight": "^7.16.0",
|
"@mantine/spotlight": "^7.16.0",
|
||||||
"@mantine/vanilla-extract": "^7.16.0",
|
"@mantine/vanilla-extract": "^7.16.0",
|
||||||
|
"@messageformat/date-skeleton": "^1.1.0",
|
||||||
"@sentry/react": "^8.43.0",
|
"@sentry/react": "^8.43.0",
|
||||||
"@tabler/icons-react": "^3.17.0",
|
"@tabler/icons-react": "^3.17.0",
|
||||||
"@tanstack/react-query": "^5.56.2",
|
"@tanstack/react-query": "^5.56.2",
|
||||||
@ -83,7 +84,7 @@
|
|||||||
"@lingui/cli": "^5.3.0",
|
"@lingui/cli": "^5.3.0",
|
||||||
"@lingui/macro": "^5.3.0",
|
"@lingui/macro": "^5.3.0",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.49.1",
|
||||||
"@types/node": "^22.6.0",
|
"@types/node": "^22.13.14",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/react": "^18.3.8",
|
"@types/react": "^18.3.8",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
@ -94,6 +95,7 @@
|
|||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^17.1.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
"rollup": "^4.0.0",
|
"rollup": "^4.0.0",
|
||||||
"rollup-plugin-license": "^3.5.3",
|
"rollup-plugin-license": "^3.5.3",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
|
@ -1,13 +1,96 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
import type TestConfigWebServer from '@playwright/test';
|
||||||
|
|
||||||
|
// Detect if running in CI
|
||||||
|
const IS_CI = !!process.env.CI;
|
||||||
|
const IS_COVERAGE = !!process.env.VITE_COVERAGE_BUILD;
|
||||||
|
|
||||||
|
// If specified, tests will be run against the production build
|
||||||
|
const IS_PRODUCTION = !!process.env.VITE_PRODUCTION_BUILD;
|
||||||
|
|
||||||
|
console.log('Running Playwright tests:');
|
||||||
|
console.log(` - CI Mode: ${IS_CI}`);
|
||||||
|
console.log(` - Coverage Mode: ${IS_COVERAGE}`);
|
||||||
|
console.log(` - Production Mode: ${IS_PRODUCTION}`);
|
||||||
|
|
||||||
|
const MAX_WORKERS: number = 3;
|
||||||
|
const MAX_RETRIES: number = 3;
|
||||||
|
|
||||||
|
/* We optionally spin-up services based on the testing mode:
|
||||||
|
*
|
||||||
|
* Local Development:
|
||||||
|
* - If running locally (developer mode), we run "vite dev" with HMR enabled
|
||||||
|
* - This allows playwright to monitor the code for changes
|
||||||
|
* - WORKERS = 1 (to avoid conflicts with HMR)
|
||||||
|
*
|
||||||
|
* CI Mode (Production):
|
||||||
|
* - In CI (GitHub actions), we run "vite build" to generate a production build
|
||||||
|
* - This build is then served by a local server for testing
|
||||||
|
* - This allows the tests to run much faster and with parallel workers
|
||||||
|
* - Run a Gunicorn multi-threaded web server to handle multiple requests
|
||||||
|
* - WORKERS = MAX_WORKERS (to speed up the tests)
|
||||||
|
*
|
||||||
|
* CI Mode (Coverage):
|
||||||
|
* - In coverage mode (GitHub actions), we cannot compile the code first
|
||||||
|
* - This is because we need to compile coverage report against ./src directory
|
||||||
|
* - So, we run "vite dev" with coverage enabled (similar to the local dev mode)
|
||||||
|
* - WORKERS = 1 (to avoid conflicts with HMR)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const devServer: TestConfigWebServer = {
|
||||||
|
command: 'yarn run dev --host --port 5173',
|
||||||
|
url: 'http://localhost:5173',
|
||||||
|
reuseExistingServer: IS_CI,
|
||||||
|
stdout: 'pipe',
|
||||||
|
stderr: 'pipe',
|
||||||
|
timeout: 120 * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
const WEB_BUILD_CMD: string =
|
||||||
|
'yarn run extract && yarn run compile && yarn run build';
|
||||||
|
|
||||||
|
// Command to spin-up the backend server
|
||||||
|
// In production mode, we want a stronger webserver to handle multiple requests
|
||||||
|
const WEB_SERVER_CMD: string = IS_PRODUCTION
|
||||||
|
? `${WEB_BUILD_CMD} && gunicorn --chdir ../backend/InvenTree --workers 8 --thread 8 --bind 127.0.0.1:8000 InvenTree.wsgi`
|
||||||
|
: 'invoke dev.server -a 127.0.0.1:8000';
|
||||||
|
|
||||||
|
const webServer: TestConfigWebServer = {
|
||||||
|
// If running in production mode, we need to build the frontend first
|
||||||
|
command: WEB_SERVER_CMD,
|
||||||
|
env: {
|
||||||
|
INVENTREE_DEBUG: 'True',
|
||||||
|
INVENTREE_PLUGINS_ENABLED: 'True',
|
||||||
|
INVENTREE_ADMIN_URL: 'test-admin',
|
||||||
|
INVENTREE_SITE_URL: 'http://localhost:8000',
|
||||||
|
INVENTREE_FRONTEND_API_HOST: 'http://localhost:8000',
|
||||||
|
INVENTREE_CORS_ORIGIN_ALLOW_ALL: 'True',
|
||||||
|
INVENTREE_COOKIE_SAMESITE: 'Lax',
|
||||||
|
INVENTREE_LOGIN_ATTEMPTS: '100'
|
||||||
|
},
|
||||||
|
url: 'http://localhost:8000/api/',
|
||||||
|
reuseExistingServer: IS_CI,
|
||||||
|
stdout: 'pipe',
|
||||||
|
stderr: 'pipe',
|
||||||
|
timeout: 120 * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverList: TestConfigWebServer[] = [];
|
||||||
|
|
||||||
|
if (!IS_PRODUCTION) {
|
||||||
|
serverList.push(devServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverList.push(webServer);
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests',
|
testDir: './tests',
|
||||||
fullyParallel: true,
|
fullyParallel: false,
|
||||||
timeout: 90000,
|
timeout: 90000,
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!IS_CI,
|
||||||
retries: process.env.CI ? 3 : 0,
|
retries: IS_CI ? MAX_RETRIES : 0,
|
||||||
workers: 1, // process.env.CI ? 3 : undefined,
|
workers: IS_PRODUCTION ? MAX_WORKERS : 1,
|
||||||
reporter: process.env.CI ? [['html', { open: 'never' }], ['github']] : 'list',
|
reporter: IS_CI ? [['html', { open: 'never' }], ['github']] : 'list',
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
projects: [
|
projects: [
|
||||||
@ -26,35 +109,11 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
webServer: [
|
webServer: serverList,
|
||||||
{
|
globalSetup: './playwright/global-setup.ts',
|
||||||
command: 'yarn run dev --host',
|
|
||||||
url: 'http://localhost:5173',
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
stdout: 'pipe',
|
|
||||||
stderr: 'pipe',
|
|
||||||
timeout: 120 * 1000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: 'invoke dev.server -a 127.0.0.1:8000',
|
|
||||||
env: {
|
|
||||||
INVENTREE_DEBUG: 'True',
|
|
||||||
INVENTREE_PLUGINS_ENABLED: 'True',
|
|
||||||
INVENTREE_ADMIN_URL: 'test-admin',
|
|
||||||
INVENTREE_SITE_URL: 'http://localhost:8000',
|
|
||||||
INVENTREE_CORS_ORIGIN_ALLOW_ALL: 'True',
|
|
||||||
INVENTREE_COOKIE_SAMESITE: 'Lax',
|
|
||||||
INVENTREE_LOGIN_ATTEMPTS: '100'
|
|
||||||
},
|
|
||||||
url: 'http://127.0.0.1:8000/api/',
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
stdout: 'pipe',
|
|
||||||
stderr: 'pipe',
|
|
||||||
timeout: 120 * 1000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
use: {
|
use: {
|
||||||
baseURL: 'http://localhost:5173',
|
baseURL: IS_PRODUCTION ? 'http://localhost:8000' : 'http://localhost:5173',
|
||||||
|
headless: IS_PRODUCTION ? true : undefined,
|
||||||
trace: 'on-first-retry'
|
trace: 'on-first-retry'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
3
src/frontend/playwright/.gitignore
vendored
Normal file
3
src/frontend/playwright/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Ignore auth cache
|
||||||
|
auth/
|
||||||
|
*.json
|
45
src/frontend/playwright/global-setup.ts
Normal file
45
src/frontend/playwright/global-setup.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { type FullConfig, chromium } from '@playwright/test';
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { doCachedLogin } from '../tests/login';
|
||||||
|
|
||||||
|
async function globalSetup(config: FullConfig) {
|
||||||
|
const authDir = path.resolve('./playwright/auth');
|
||||||
|
|
||||||
|
if (fs.existsSync(authDir)) {
|
||||||
|
// Clear out the cached authentication states
|
||||||
|
fs.rm(path.resolve('./playwright/auth'), { recursive: true }, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Failed to clear out cached authentication states:', err);
|
||||||
|
} else {
|
||||||
|
console.log('Removed cached authentication states');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform login for each user
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
|
||||||
|
await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
|
await doCachedLogin(browser, {
|
||||||
|
username: 'allaccess',
|
||||||
|
password: 'nolimits'
|
||||||
|
});
|
||||||
|
|
||||||
|
await doCachedLogin(browser, {
|
||||||
|
username: 'reader',
|
||||||
|
password: 'readonly'
|
||||||
|
});
|
||||||
|
|
||||||
|
await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
|
password: 'wizardstaff'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default globalSetup;
|
@ -10,9 +10,9 @@ export const api = axios.create({});
|
|||||||
* Setup default settings for the Axios API instance.
|
* Setup default settings for the Axios API instance.
|
||||||
*/
|
*/
|
||||||
export function setApiDefaults() {
|
export function setApiDefaults() {
|
||||||
const { host } = useLocalState.getState();
|
const { getHost } = useLocalState.getState();
|
||||||
|
|
||||||
api.defaults.baseURL = host;
|
api.defaults.baseURL = getHost();
|
||||||
api.defaults.timeout = 5000;
|
api.defaults.timeout = 5000;
|
||||||
|
|
||||||
api.defaults.withCredentials = true;
|
api.defaults.withCredentials = true;
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import { ActionIcon } from '@mantine/core';
|
|
||||||
import { IconDeviceFloppy, IconEdit } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
export function EditButton({
|
|
||||||
setEditing,
|
|
||||||
editing,
|
|
||||||
disabled,
|
|
||||||
saveIcon
|
|
||||||
}: Readonly<{
|
|
||||||
setEditing: (value?: React.SetStateAction<boolean> | undefined) => void;
|
|
||||||
editing: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
saveIcon?: JSX.Element;
|
|
||||||
}>) {
|
|
||||||
saveIcon = saveIcon || <IconDeviceFloppy />;
|
|
||||||
return (
|
|
||||||
<ActionIcon
|
|
||||||
onClick={() => setEditing()}
|
|
||||||
disabled={disabled}
|
|
||||||
variant='default'
|
|
||||||
>
|
|
||||||
{editing ? saveIcon : <IconEdit />}
|
|
||||||
</ActionIcon>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,10 +1,19 @@
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { ActionIcon, Divider, Group, Select, Table, Text } from '@mantine/core';
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Divider,
|
||||||
|
Group,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
Text,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
import { useToggle } from '@mantine/hooks';
|
import { useToggle } from '@mantine/hooks';
|
||||||
import {
|
import {
|
||||||
IconApi,
|
IconApi,
|
||||||
IconCheck,
|
IconCircleCheck,
|
||||||
|
IconEdit,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
IconPlugConnected,
|
IconPlugConnected,
|
||||||
IconServer,
|
IconServer,
|
||||||
@ -15,7 +24,7 @@ import { Wrapper } from '../../pages/Auth/Layout';
|
|||||||
import { useServerApiState } from '../../states/ApiState';
|
import { useServerApiState } from '../../states/ApiState';
|
||||||
import { useLocalState } from '../../states/LocalState';
|
import { useLocalState } from '../../states/LocalState';
|
||||||
import type { HostList } from '../../states/states';
|
import type { HostList } from '../../states/states';
|
||||||
import { EditButton } from '../buttons/EditButton';
|
import { ActionButton } from '../buttons/ActionButton';
|
||||||
import { HostOptionsForm } from './HostOptionsForm';
|
import { HostOptionsForm } from './HostOptionsForm';
|
||||||
|
|
||||||
export function InstanceOptions({
|
export function InstanceOptions({
|
||||||
@ -27,7 +36,7 @@ export function InstanceOptions({
|
|||||||
ChangeHost: (newHost: string | null) => void;
|
ChangeHost: (newHost: string | null) => void;
|
||||||
setHostEdit: () => void;
|
setHostEdit: () => void;
|
||||||
}>) {
|
}>) {
|
||||||
const [HostListEdit, setHostListEdit] = useToggle([false, true] as const);
|
const [hostListEdit, setHostListEdit] = useToggle([false, true] as const);
|
||||||
const [setHost, setHostList, hostList] = useLocalState((state) => [
|
const [setHost, setHostList, hostList] = useLocalState((state) => [
|
||||||
state.setHost,
|
state.setHost,
|
||||||
state.setHostList,
|
state.setHostList,
|
||||||
@ -48,27 +57,36 @@ export function InstanceOptions({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper titleText={t`Select Server`} smallPadding>
|
<Wrapper titleText={t`Select Server`} smallPadding>
|
||||||
<Group gap='xs' wrap='nowrap'>
|
<Group gap='xs' justify='space-between' wrap='nowrap'>
|
||||||
<Select
|
<Select
|
||||||
|
style={{ width: '100%' }}
|
||||||
value={hostKey}
|
value={hostKey}
|
||||||
onChange={ChangeHost}
|
onChange={ChangeHost}
|
||||||
data={hostListData}
|
data={hostListData}
|
||||||
disabled={HostListEdit}
|
disabled={hostListEdit}
|
||||||
/>
|
/>
|
||||||
<EditButton
|
<Group gap='xs' wrap='nowrap'>
|
||||||
setEditing={setHostListEdit}
|
<Tooltip label={t`Edit host options`} position='top'>
|
||||||
editing={HostListEdit}
|
<ActionButton
|
||||||
disabled={HostListEdit}
|
variant='transparent'
|
||||||
|
disabled={hostListEdit}
|
||||||
|
onClick={setHostListEdit}
|
||||||
|
icon={<IconEdit />}
|
||||||
/>
|
/>
|
||||||
<EditButton
|
</Tooltip>
|
||||||
setEditing={setHostEdit}
|
<Tooltip label={t`Save host selection`} position='top'>
|
||||||
editing={true}
|
<ActionButton
|
||||||
disabled={HostListEdit}
|
variant='transparent'
|
||||||
saveIcon={<IconCheck />}
|
onClick={setHostEdit}
|
||||||
|
disabled={hostListEdit}
|
||||||
|
icon={<IconCircleCheck />}
|
||||||
|
color='green'
|
||||||
/>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{HostListEdit ? (
|
{hostListEdit ? (
|
||||||
<>
|
<>
|
||||||
<Divider my={'sm'} />
|
<Divider my={'sm'} />
|
||||||
<Text>
|
<Text>
|
||||||
|
@ -17,11 +17,11 @@ interface ApiImageProps extends ImageProps {
|
|||||||
* Construct an image container which will load and display the image
|
* Construct an image container which will load and display the image
|
||||||
*/
|
*/
|
||||||
export function ApiImage(props: Readonly<ApiImageProps>) {
|
export function ApiImage(props: Readonly<ApiImageProps>) {
|
||||||
const { host } = useLocalState.getState();
|
const { getHost } = useLocalState.getState();
|
||||||
|
|
||||||
const imageUrl = useMemo(() => {
|
const imageUrl = useMemo(() => {
|
||||||
return generateUrl(props.src, host);
|
return generateUrl(props.src, getHost());
|
||||||
}, [host, props.src]);
|
}, [getHost, props.src]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
@ -65,7 +65,7 @@ export const doBasicLogin = async (
|
|||||||
password: string,
|
password: string,
|
||||||
navigate: NavigateFunction
|
navigate: NavigateFunction
|
||||||
) => {
|
) => {
|
||||||
const { host } = useLocalState.getState();
|
const { getHost } = useLocalState.getState();
|
||||||
const { clearUserState, setAuthenticated, fetchUserState } =
|
const { clearUserState, setAuthenticated, fetchUserState } =
|
||||||
useUserState.getState();
|
useUserState.getState();
|
||||||
const { setAuthContext } = useServerApiState.getState();
|
const { setAuthContext } = useServerApiState.getState();
|
||||||
@ -80,6 +80,8 @@ export const doBasicLogin = async (
|
|||||||
let loginDone = false;
|
let loginDone = false;
|
||||||
let success = false;
|
let success = false;
|
||||||
|
|
||||||
|
const host: string = getHost();
|
||||||
|
|
||||||
// Attempt login with
|
// Attempt login with
|
||||||
await api
|
await api
|
||||||
.post(
|
.post(
|
||||||
@ -156,7 +158,7 @@ export const doLogout = async (navigate: NavigateFunction) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const doSimpleLogin = async (email: string) => {
|
export const doSimpleLogin = async (email: string) => {
|
||||||
const { host } = useLocalState.getState();
|
const { getHost } = useLocalState.getState();
|
||||||
const mail = await axios
|
const mail = await axios
|
||||||
.post(
|
.post(
|
||||||
apiUrl(ApiEndpoints.user_simple_login),
|
apiUrl(ApiEndpoints.user_simple_login),
|
||||||
@ -164,7 +166,7 @@ export const doSimpleLogin = async (email: string) => {
|
|||||||
email: email
|
email: email
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseURL: host,
|
baseURL: getHost(),
|
||||||
timeout: 2000
|
timeout: 2000
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -36,10 +36,12 @@ export function getDetailUrl(
|
|||||||
* Returns the edit view URL for a given model type
|
* Returns the edit view URL for a given model type
|
||||||
*/
|
*/
|
||||||
export function generateUrl(url: string | URL, base?: string): string {
|
export function generateUrl(url: string | URL, base?: string): string {
|
||||||
const { host } = useLocalState.getState();
|
const { getHost } = useLocalState.getState();
|
||||||
|
|
||||||
let newUrl: string | URL = url;
|
let newUrl: string | URL = url;
|
||||||
|
|
||||||
|
const host: string = getHost();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (base) {
|
if (base) {
|
||||||
newUrl = new URL(url, base).toString();
|
newUrl = new URL(url, base).toString();
|
||||||
|
@ -22,7 +22,8 @@ declare global {
|
|||||||
server_list: HostList;
|
server_list: HostList;
|
||||||
default_server: string;
|
default_server: string;
|
||||||
show_server_selector: boolean;
|
show_server_selector: boolean;
|
||||||
base_url: string;
|
base_url?: string;
|
||||||
|
api_host?: string;
|
||||||
sentry_dsn?: string;
|
sentry_dsn?: string;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
};
|
};
|
||||||
@ -30,12 +31,14 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Running in dev mode (i.e. vite)
|
||||||
export const IS_DEV = import.meta.env.DEV;
|
export const IS_DEV = import.meta.env.DEV;
|
||||||
export const IS_DEMO = import.meta.env.VITE_DEMO === 'true';
|
export const IS_DEMO = import.meta.env.VITE_DEMO === 'true';
|
||||||
export const IS_DEV_OR_DEMO = IS_DEV || IS_DEMO;
|
export const IS_DEV_OR_DEMO = IS_DEV || IS_DEMO;
|
||||||
|
|
||||||
// Filter out any settings that are not defined
|
// Filter out any settings that are not defined
|
||||||
const loaded_vals = (window.INVENTREE_SETTINGS || {}) as any;
|
const loaded_vals = (window.INVENTREE_SETTINGS || {}) as any;
|
||||||
|
|
||||||
Object.keys(loaded_vals).forEach((key) => {
|
Object.keys(loaded_vals).forEach((key) => {
|
||||||
if (loaded_vals[key] === undefined) {
|
if (loaded_vals[key] === undefined) {
|
||||||
delete loaded_vals[key];
|
delete loaded_vals[key];
|
||||||
@ -48,13 +51,9 @@ Object.keys(loaded_vals).forEach((key) => {
|
|||||||
|
|
||||||
window.INVENTREE_SETTINGS = {
|
window.INVENTREE_SETTINGS = {
|
||||||
server_list: {
|
server_list: {
|
||||||
'mantine-cqj63coxn': {
|
|
||||||
host: `${window.location.origin}/`,
|
|
||||||
name: 'Current Server'
|
|
||||||
},
|
|
||||||
...(IS_DEV
|
...(IS_DEV
|
||||||
? {
|
? {
|
||||||
'mantine-2j5j5j5j5': {
|
'server-localhost': {
|
||||||
host: 'http://localhost:8000',
|
host: 'http://localhost:8000',
|
||||||
name: 'Localhost'
|
name: 'Localhost'
|
||||||
}
|
}
|
||||||
@ -62,21 +61,25 @@ window.INVENTREE_SETTINGS = {
|
|||||||
: {}),
|
: {}),
|
||||||
...(IS_DEV_OR_DEMO
|
...(IS_DEV_OR_DEMO
|
||||||
? {
|
? {
|
||||||
'mantine-u56l5jt85': {
|
'server-demo': {
|
||||||
host: 'https://demo.inventree.org/',
|
host: 'https://demo.inventree.org/',
|
||||||
name: 'InvenTree Demo'
|
name: 'InvenTree Demo'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {}),
|
||||||
|
'server-current': {
|
||||||
|
host: `${window.location.origin}/`,
|
||||||
|
name: 'Current Server'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
default_server: IS_DEV
|
default_server: IS_DEV
|
||||||
? 'mantine-2j5j5j5j5'
|
? 'server-localhost'
|
||||||
: IS_DEMO
|
: IS_DEMO
|
||||||
? 'mantine-u56l5jt85'
|
? 'server-demo'
|
||||||
: 'mantine-cqj63coxn',
|
: 'server-current',
|
||||||
show_server_selector: IS_DEV_OR_DEMO,
|
show_server_selector: IS_DEV_OR_DEMO,
|
||||||
|
|
||||||
// merge in settings that are already set via django's spa_view or for development
|
// Merge in settings that are already set via django's spa_view or for development
|
||||||
...loaded_vals
|
...loaded_vals
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ interface LocalStateProps {
|
|||||||
autoupdate: boolean;
|
autoupdate: boolean;
|
||||||
toggleAutoupdate: () => void;
|
toggleAutoupdate: () => void;
|
||||||
host: string;
|
host: string;
|
||||||
|
getHost: () => string;
|
||||||
setHost: (newHost: string, newHostKey: string) => void;
|
setHost: (newHost: string, newHostKey: string) => void;
|
||||||
hostKey: string;
|
hostKey: string;
|
||||||
hostList: HostList;
|
hostList: HostList;
|
||||||
@ -65,6 +66,28 @@ export const useLocalState = create<LocalStateProps>()(
|
|||||||
toggleAutoupdate: () =>
|
toggleAutoupdate: () =>
|
||||||
set((state) => ({ autoupdate: !state.autoupdate })),
|
set((state) => ({ autoupdate: !state.autoupdate })),
|
||||||
host: '',
|
host: '',
|
||||||
|
getHost: () => {
|
||||||
|
// Retrieve and validate the API host
|
||||||
|
const state = get();
|
||||||
|
|
||||||
|
let host = state.host;
|
||||||
|
|
||||||
|
// If the server provides an override for the host, use that
|
||||||
|
if (window.INVENTREE_SETTINGS?.api_host) {
|
||||||
|
host = window.INVENTREE_SETTINGS.api_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no host is provided, use the first host in the list
|
||||||
|
if (!host && Object.keys(state.hostList).length) {
|
||||||
|
host = Object.values(state.hostList)[0].host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no host is provided, fallback to using the current URL (default)
|
||||||
|
if (!host) {
|
||||||
|
host = window.location.origin;
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
},
|
||||||
setHost: (newHost, newHostKey) =>
|
setHost: (newHost, newHostKey) =>
|
||||||
set({ host: newHost, hostKey: newHostKey }),
|
set({ host: newHost, hostKey: newHostKey }),
|
||||||
hostKey: '',
|
hostKey: '',
|
||||||
|
@ -86,8 +86,7 @@ export const navigate = async (page, url: string) => {
|
|||||||
url = `${baseUrl}/${url}`;
|
url = `${baseUrl}/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
await page.goto(url);
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +97,6 @@ export const loadTab = async (page, tabName) => {
|
|||||||
.getByLabel(/panel-tabs-/)
|
.getByLabel(/panel-tabs-/)
|
||||||
.getByRole('tab', { name: tabName })
|
.getByRole('tab', { name: tabName })
|
||||||
.click();
|
.click();
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Activate "table" view in certain contexts
|
// Activate "table" view in certain contexts
|
||||||
@ -121,5 +119,4 @@ export const globalSearch = async (page, query) => {
|
|||||||
await page.getByLabel('global-search-input').clear();
|
await page.getByLabel('global-search-input').clear();
|
||||||
await page.getByPlaceholder('Enter search text').fill(query);
|
await page.getByPlaceholder('Enter search text').fill(query);
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
import type { Browser, Page } from '@playwright/test';
|
||||||
import { expect } from './baseFixtures.js';
|
import { expect } from './baseFixtures.js';
|
||||||
import { baseUrl, logoutUrl, user } from './defaults';
|
import { user } from './defaults';
|
||||||
import { navigate } from './helpers.js';
|
import { navigate } from './helpers.js';
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform form based login operation from the "login" URL
|
* Perform form based login operation from the "login" URL
|
||||||
*/
|
*/
|
||||||
@ -9,7 +13,8 @@ export const doLogin = async (page, username?: string, password?: string) => {
|
|||||||
username = username ?? user.username;
|
username = username ?? user.username;
|
||||||
password = password ?? user.password;
|
password = password ?? user.password;
|
||||||
|
|
||||||
await navigate(page, logoutUrl);
|
await page.goto('http://localhost:8000/web/logout', { waituntil: 'load' });
|
||||||
|
|
||||||
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
||||||
await page.waitForURL('**/web/login');
|
await page.waitForURL('**/web/login');
|
||||||
await page.getByLabel('username').fill(username);
|
await page.getByLabel('username').fill(username);
|
||||||
@ -19,31 +24,74 @@ export const doLogin = async (page, username?: string, password?: string) => {
|
|||||||
await page.waitForTimeout(250);
|
await page.waitForTimeout(250);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface CachedLoginOptions {
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set of users allowed to do cached login
|
||||||
|
// This is to prevent tests from running with the wrong user
|
||||||
|
const ALLOWED_USERS: string[] = ['admin', 'allaccess', 'reader', 'steven'];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform a quick login based on passing URL parameters
|
* Perform a quick login based on passing URL parameters
|
||||||
*/
|
*/
|
||||||
export const doQuickLogin = async (
|
export const doCachedLogin = async (
|
||||||
page,
|
browser: Browser,
|
||||||
username?: string,
|
options?: CachedLoginOptions
|
||||||
password?: string,
|
): Promise<Page> => {
|
||||||
url?: string
|
const username = options?.username ?? user.username;
|
||||||
) => {
|
const password = options?.password ?? user.password;
|
||||||
username = username ?? user.username;
|
const url = options?.url ?? '';
|
||||||
password = password ?? user.password;
|
|
||||||
url = url ?? baseUrl;
|
|
||||||
|
|
||||||
await navigate(page, `${url}/login?login=${username}&password=${password}`);
|
// FAIL if an unsupported username is provided
|
||||||
await page.waitForURL('**/web/home');
|
if (!ALLOWED_USERS.includes(username)) {
|
||||||
|
throw new Error(`Invalid username provided to doCachedLogin: ${username}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the login state locally - and share between tests
|
||||||
|
const fn = path.resolve(`./playwright/auth/${username}.json`);
|
||||||
|
|
||||||
|
if (fs.existsSync(fn)) {
|
||||||
|
const page = await browser.newPage({
|
||||||
|
storageState: fn
|
||||||
|
});
|
||||||
|
console.log(`Using cached login state for ${username}`);
|
||||||
|
await navigate(page, url);
|
||||||
|
await page.waitForURL('**/web/**');
|
||||||
|
await page.waitForLoadState('load');
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new blank page
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
console.log(`No cache found - logging in for ${username}`);
|
||||||
|
|
||||||
|
// Ensure we start from the login page
|
||||||
|
await page.goto('http://localhost:8000/web/', { waitUntil: 'load' });
|
||||||
|
|
||||||
|
await doLogin(page, username, password);
|
||||||
await page.getByLabel('navigation-menu').waitFor({ timeout: 5000 });
|
await page.getByLabel('navigation-menu').waitFor({ timeout: 5000 });
|
||||||
await page.getByText(/InvenTree Demo Server -/).waitFor();
|
await page.getByText(/InvenTree Demo Server -/).waitFor();
|
||||||
|
await page.waitForURL('**/web/**');
|
||||||
|
|
||||||
// Wait for the dashboard to load
|
// Wait for the dashboard to load
|
||||||
await page.getByText('No widgets selected').waitFor();
|
await page.getByText('No widgets selected').waitFor();
|
||||||
await page.waitForTimeout(250);
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Cache the login state
|
||||||
|
await page.context().storageState({ path: fn });
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
await navigate(page, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return page;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doLogout = async (page) => {
|
export const doLogout = async (page) => {
|
||||||
await navigate(page, 'logout');
|
await page.goto('http://localhost:8000/web/logout', { waitUntil: 'load' });
|
||||||
await page.waitForURL('**/web/login');
|
await page.waitForURL('**/web/login');
|
||||||
};
|
};
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
import { test } from './baseFixtures.js';
|
|
||||||
import { doQuickLogin } from './login.js';
|
|
||||||
|
|
||||||
test('Modals - Admin', async ({ page }) => {
|
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
|
||||||
|
|
||||||
// use server info
|
|
||||||
await page.getByLabel('open-spotlight').click();
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'Server Information About this InvenTree instance'
|
|
||||||
})
|
|
||||||
.click();
|
|
||||||
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
|
||||||
await page.getByRole('button', { name: 'Close' }).click();
|
|
||||||
|
|
||||||
await page.waitForURL('**/web/home');
|
|
||||||
|
|
||||||
// use license info
|
|
||||||
await page.getByLabel('open-spotlight').click();
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'License Information Licenses for dependencies of the service'
|
|
||||||
})
|
|
||||||
.click();
|
|
||||||
await page.getByText('License Information').first().waitFor();
|
|
||||||
await page.getByRole('tab', { name: 'backend Packages' }).click();
|
|
||||||
await page.getByRole('button', { name: 'Babel BSD License' }).click();
|
|
||||||
await page
|
|
||||||
.getByText('by the Babel Team, see AUTHORS for more information')
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'frontend Packages' }).click();
|
|
||||||
await page.getByRole('button', { name: '@sentry/core MIT' }).click();
|
|
||||||
await page
|
|
||||||
.getByLabel('@sentry/coreMIT')
|
|
||||||
.getByText('Copyright (c) 2019')
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
await page
|
|
||||||
.getByLabel('License Information')
|
|
||||||
.getByRole('button')
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// use about
|
|
||||||
await page.getByLabel('open-spotlight').click();
|
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'About InvenTree About the InvenTree org' })
|
|
||||||
.click();
|
|
||||||
await page.getByRole('cell', { name: 'InvenTree Version' }).click();
|
|
||||||
});
|
|
@ -8,13 +8,15 @@ import {
|
|||||||
navigate,
|
navigate,
|
||||||
setTableChoiceFilter
|
setTableChoiceFilter
|
||||||
} from '../helpers.ts';
|
} from '../helpers.ts';
|
||||||
import { doQuickLogin } from '../login.ts';
|
import { doCachedLogin } from '../login.ts';
|
||||||
|
|
||||||
test('Build Order - Basic Tests', async ({ page }) => {
|
test('Build Order - Basic Tests', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// Navigate to the correct build order
|
// Navigate to the correct build order
|
||||||
await page.getByRole('tab', { name: 'Manufacturing', exact: true }).click();
|
await page.getByRole('tab', { name: 'Manufacturing' }).click();
|
||||||
|
await page.waitForURL('**/manufacturing/index/**');
|
||||||
|
|
||||||
await loadTab(page, 'Build Orders');
|
await loadTab(page, 'Build Orders');
|
||||||
|
|
||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
@ -91,8 +93,8 @@ test('Build Order - Basic Tests', async ({ page }) => {
|
|||||||
.waitFor();
|
.waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Calendar', async ({ page }) => {
|
test('Build Order - Calendar', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/index/buildorders');
|
await navigate(page, 'manufacturing/index/buildorders');
|
||||||
await activateCalendarView(page);
|
await activateCalendarView(page);
|
||||||
@ -114,8 +116,8 @@ test('Build Order - Calendar', async ({ page }) => {
|
|||||||
await page.context().close();
|
await page.context().close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Edit', async ({ page }) => {
|
test('Build Order - Edit', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/build-order/22/');
|
await navigate(page, 'manufacturing/build-order/22/');
|
||||||
|
|
||||||
@ -141,8 +143,8 @@ test('Build Order - Edit', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Build Outputs', async ({ page }) => {
|
test('Build Order - Build Outputs', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/index/');
|
await navigate(page, 'manufacturing/index/');
|
||||||
await loadTab(page, 'Build Orders');
|
await loadTab(page, 'Build Orders');
|
||||||
@ -217,8 +219,8 @@ test('Build Order - Build Outputs', async ({ page }) => {
|
|||||||
await page.getByText('Build outputs have been completed').waitFor();
|
await page.getByText('Build outputs have been completed').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Allocation', async ({ page }) => {
|
test('Build Order - Allocation', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/build-order/1/line-items');
|
await navigate(page, 'manufacturing/build-order/1/line-items');
|
||||||
|
|
||||||
@ -317,8 +319,8 @@ test('Build Order - Allocation', async ({ page }) => {
|
|||||||
.waitFor();
|
.waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Filters', async ({ page }) => {
|
test('Build Order - Filters', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/index/buildorders');
|
await navigate(page, 'manufacturing/index/buildorders');
|
||||||
|
|
||||||
@ -351,8 +353,8 @@ test('Build Order - Filters', async ({ page }) => {
|
|||||||
await page.getByText('Pending Approval').first().waitFor();
|
await page.getByText('Pending Approval').first().waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Build Order - Duplicate', async ({ page }) => {
|
test('Build Order - Duplicate', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'manufacturing/build-order/24/details');
|
await navigate(page, 'manufacturing/build-order/24/details');
|
||||||
await page.getByLabel('action-menu-build-order-').click();
|
await page.getByLabel('action-menu-build-order-').click();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { test } from '../baseFixtures.js';
|
import { test } from '../baseFixtures.js';
|
||||||
import { loadTab, navigate } from '../helpers.js';
|
import { loadTab, navigate } from '../helpers.js';
|
||||||
import { doQuickLogin } from '../login.js';
|
import { doCachedLogin } from '../login.js';
|
||||||
|
|
||||||
test('Company', async ({ page }) => {
|
test('Company', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await navigate(page, 'company/1/details');
|
await navigate(page, 'company/1/details');
|
||||||
await page.getByLabel('Details').getByText('DigiKey Electronics').waitFor();
|
await page.getByLabel('Details').getByText('DigiKey Electronics').waitFor();
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { test } from '../baseFixtures.js';
|
import { test } from '../baseFixtures.js';
|
||||||
import { loadTab, navigate } from '../helpers.js';
|
import { loadTab, navigate } from '../helpers.js';
|
||||||
import { doQuickLogin } from '../login.js';
|
import { doCachedLogin } from '../login.js';
|
||||||
|
|
||||||
test('Core User/Group/Contact', async ({ page }) => {
|
test('Core User/Group/Contact', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// groups
|
// groups
|
||||||
await navigate(page, '/core');
|
await navigate(page, '/core');
|
||||||
await page.getByText('System Overview', { exact: true }).click();
|
await page.getByText('System Overview', { exact: true }).first().click();
|
||||||
await loadTab(page, 'Groups');
|
await loadTab(page, 'Groups');
|
||||||
await page.getByRole('cell', { name: 'all access' }).click();
|
await page.getByRole('cell', { name: 'all access' }).click();
|
||||||
await page.getByText('Group: all access', { exact: true }).click();
|
await page.getByText('Group: all access', { exact: true }).click();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { test } from '../baseFixtures.js';
|
import { test } from '../baseFixtures.js';
|
||||||
import { doQuickLogin } from '../login.js';
|
import { doCachedLogin } from '../login.js';
|
||||||
import { setPluginState } from '../settings.js';
|
import { setPluginState } from '../settings.js';
|
||||||
|
|
||||||
test('Dashboard - Basic', async ({ page }) => {
|
test('Dashboard - Basic', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByText('Use the menu to add widgets').waitFor();
|
await page.getByText('Use the menu to add widgets').waitFor();
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ test('Dashboard - Basic', async ({ page }) => {
|
|||||||
await page.getByLabel('dashboard-accept-layout').click();
|
await page.getByLabel('dashboard-accept-layout').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Dashboard - Plugins', async ({ page, request }) => {
|
test('Dashboard - Plugins', async ({ browser, request }) => {
|
||||||
// Ensure that the "SampleUI" plugin is enabled
|
// Ensure that the "SampleUI" plugin is enabled
|
||||||
await setPluginState({
|
await setPluginState({
|
||||||
request,
|
request,
|
||||||
@ -43,7 +43,7 @@ test('Dashboard - Plugins', async ({ page, request }) => {
|
|||||||
state: true
|
state: true
|
||||||
});
|
});
|
||||||
|
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// Add a dashboard widget from the SampleUI plugin
|
// Add a dashboard widget from the SampleUI plugin
|
||||||
await page.getByLabel('dashboard-menu').click();
|
await page.getByLabel('dashboard-menu').click();
|
||||||
|
@ -5,15 +5,17 @@ import {
|
|||||||
loadTab,
|
loadTab,
|
||||||
navigate
|
navigate
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { doQuickLogin } from '../login';
|
import { doCachedLogin } from '../login';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CHeck each panel tab for the "Parts" page
|
* CHeck each panel tab for the "Parts" page
|
||||||
*/
|
*/
|
||||||
test('Parts - Tabs', async ({ page }) => {
|
test('Parts - Tabs', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Parts' }).click();
|
await page.getByRole('tab', { name: 'Parts' }).click();
|
||||||
|
await page.waitForURL('**/part/category/index/**');
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByLabel('panel-tabs-partcategory')
|
.getByLabel('panel-tabs-partcategory')
|
||||||
.getByRole('tab', { name: 'Parts' })
|
.getByRole('tab', { name: 'Parts' })
|
||||||
@ -56,10 +58,8 @@ test('Parts - Tabs', async ({ page }) => {
|
|||||||
await loadTab(page, 'Build Orders');
|
await loadTab(page, 'Build Orders');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Manufacturer Parts', async ({ page }) => {
|
test('Parts - Manufacturer Parts', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/84/suppliers' });
|
||||||
|
|
||||||
await navigate(page, 'part/84/suppliers');
|
|
||||||
|
|
||||||
await loadTab(page, 'Suppliers');
|
await loadTab(page, 'Suppliers');
|
||||||
await page.getByText('Hammond Manufacturing').click();
|
await page.getByText('Hammond Manufacturing').click();
|
||||||
@ -69,10 +69,8 @@ test('Parts - Manufacturer Parts', async ({ page }) => {
|
|||||||
await page.getByText('1551ACLR - 1551ACLR').waitFor();
|
await page.getByText('1551ACLR - 1551ACLR').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Supplier Parts', async ({ page }) => {
|
test('Parts - Supplier Parts', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/15/suppliers' });
|
||||||
|
|
||||||
await navigate(page, 'part/15/suppliers');
|
|
||||||
|
|
||||||
await loadTab(page, 'Suppliers');
|
await loadTab(page, 'Suppliers');
|
||||||
await page.getByRole('cell', { name: 'DIG-84670-SJI' }).click();
|
await page.getByRole('cell', { name: 'DIG-84670-SJI' }).click();
|
||||||
@ -82,11 +80,8 @@ test('Parts - Supplier Parts', async ({ page }) => {
|
|||||||
await page.getByText('DIG-84670-SJI - R_550R_0805_1%').waitFor();
|
await page.getByText('DIG-84670-SJI - R_550R_0805_1%').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Locking', async ({ page }) => {
|
test('Parts - Locking', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/104/bom' });
|
||||||
|
|
||||||
// Navigate to a known assembly which is *not* locked
|
|
||||||
await navigate(page, 'part/104/bom');
|
|
||||||
await loadTab(page, 'Bill of Materials');
|
await loadTab(page, 'Bill of Materials');
|
||||||
await page.getByLabel('action-button-add-bom-item').waitFor();
|
await page.getByLabel('action-button-add-bom-item').waitFor();
|
||||||
await loadTab(page, 'Parameters');
|
await loadTab(page, 'Parameters');
|
||||||
@ -108,11 +103,9 @@ test('Parts - Locking', async ({ page }) => {
|
|||||||
await page.getByText('Part parameters cannot be').waitFor();
|
await page.getByText('Part parameters cannot be').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Allocations', async ({ page }) => {
|
test('Parts - Allocations', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
|
||||||
|
|
||||||
// Let's look at the allocations for a single stock item
|
// Let's look at the allocations for a single stock item
|
||||||
await navigate(page, 'stock/item/324/');
|
const page = await doCachedLogin(browser, { url: 'stock/item/324/' });
|
||||||
await loadTab(page, 'Allocations');
|
await loadTab(page, 'Allocations');
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Build Order Allocations' }).waitFor();
|
await page.getByRole('button', { name: 'Build Order Allocations' }).waitFor();
|
||||||
@ -173,11 +166,9 @@ test('Parts - Allocations', async ({ page }) => {
|
|||||||
await page.keyboard.press('Escape');
|
await page.keyboard.press('Escape');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Pricing (Nothing, BOM)', async ({ page }) => {
|
test('Parts - Pricing (Nothing, BOM)', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
|
||||||
|
|
||||||
// Part with no history
|
// Part with no history
|
||||||
await navigate(page, 'part/82/pricing');
|
const page = await doCachedLogin(browser, { url: 'part/82/pricing' });
|
||||||
|
|
||||||
await page.getByText('Small plastic enclosure, black').waitFor();
|
await page.getByText('Small plastic enclosure, black').waitFor();
|
||||||
await loadTab(page, 'Part Pricing');
|
await loadTab(page, 'Part Pricing');
|
||||||
@ -223,11 +214,9 @@ test('Parts - Pricing (Nothing, BOM)', async ({ page }) => {
|
|||||||
await page.waitForURL('**/part/98/**');
|
await page.waitForURL('**/part/98/**');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Pricing (Supplier)', async ({ page }) => {
|
test('Parts - Pricing (Supplier)', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/55/pricing' });
|
||||||
|
|
||||||
// Part
|
|
||||||
await navigate(page, 'part/55/pricing');
|
|
||||||
await page.getByText('Ceramic capacitor, 100nF in').waitFor();
|
await page.getByText('Ceramic capacitor, 100nF in').waitFor();
|
||||||
await loadTab(page, 'Part Pricing');
|
await loadTab(page, 'Part Pricing');
|
||||||
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
||||||
@ -249,11 +238,8 @@ test('Parts - Pricing (Supplier)', async ({ page }) => {
|
|||||||
// await page.waitForURL('**/purchasing/supplier-part/697/');
|
// await page.waitForURL('**/purchasing/supplier-part/697/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Pricing (Variant)', async ({ page }) => {
|
test('Parts - Pricing (Variant)', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/106/pricing' });
|
||||||
|
|
||||||
// Part
|
|
||||||
await navigate(page, 'part/106/pricing');
|
|
||||||
await page.getByText('A chair - available in multiple colors').waitFor();
|
await page.getByText('A chair - available in multiple colors').waitFor();
|
||||||
await loadTab(page, 'Part Pricing');
|
await loadTab(page, 'Part Pricing');
|
||||||
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
||||||
@ -275,11 +261,8 @@ test('Parts - Pricing (Variant)', async ({ page }) => {
|
|||||||
await page.waitForURL('**/part/109/**');
|
await page.waitForURL('**/part/109/**');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Pricing (Internal)', async ({ page }) => {
|
test('Parts - Pricing (Internal)', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/65/pricing' });
|
||||||
|
|
||||||
// Part
|
|
||||||
await navigate(page, 'part/65/pricing');
|
|
||||||
await page.getByText('Socket head cap screw, M2').waitFor();
|
await page.getByText('Socket head cap screw, M2').waitFor();
|
||||||
await loadTab(page, 'Part Pricing');
|
await loadTab(page, 'Part Pricing');
|
||||||
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
||||||
@ -300,11 +283,9 @@ test('Parts - Pricing (Internal)', async ({ page }) => {
|
|||||||
await page.getByText('Part *M2x4 SHCSSocket head').click();
|
await page.getByText('Part *M2x4 SHCSSocket head').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Pricing (Purchase)', async ({ page }) => {
|
test('Parts - Pricing (Purchase)', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/69/pricing' });
|
||||||
|
|
||||||
// Part
|
|
||||||
await navigate(page, 'part/69/pricing');
|
|
||||||
await page.getByText('1.25mm Pitch, PicoBlade PCB').waitFor();
|
await page.getByText('1.25mm Pitch, PicoBlade PCB').waitFor();
|
||||||
await loadTab(page, 'Part Pricing');
|
await loadTab(page, 'Part Pricing');
|
||||||
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor();
|
||||||
@ -322,10 +303,8 @@ test('Parts - Pricing (Purchase)', async ({ page }) => {
|
|||||||
await page.getByText('2022-04-29').waitFor();
|
await page.getByText('2022-04-29').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Attachments', async ({ page }) => {
|
test('Parts - Attachments', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/69/attachments' });
|
||||||
|
|
||||||
await navigate(page, 'part/69/attachments');
|
|
||||||
|
|
||||||
// Submit a new external link
|
// Submit a new external link
|
||||||
await page.getByLabel('action-button-add-external-').click();
|
await page.getByLabel('action-button-add-external-').click();
|
||||||
@ -344,10 +323,8 @@ test('Parts - Attachments', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Parameters', async ({ page }) => {
|
test('Parts - Parameters', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/69/parameters' });
|
||||||
|
|
||||||
await navigate(page, 'part/69/parameters');
|
|
||||||
|
|
||||||
// Create a new template
|
// Create a new template
|
||||||
await page.getByLabel('action-button-add-parameter').click();
|
await page.getByLabel('action-button-add-parameter').click();
|
||||||
@ -371,10 +348,8 @@ test('Parts - Parameters', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Notes', async ({ page }) => {
|
test('Parts - Notes', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/69/notes' });
|
||||||
|
|
||||||
await navigate(page, 'part/69/notes');
|
|
||||||
|
|
||||||
// Enable editing
|
// Enable editing
|
||||||
await page.getByLabel('Enable Editing').waitFor();
|
await page.getByLabel('Enable Editing').waitFor();
|
||||||
@ -393,20 +368,16 @@ test('Parts - Notes', async ({ page }) => {
|
|||||||
await page.getByLabel('Close Editor').waitFor();
|
await page.getByLabel('Close Editor').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - 404', async ({ page }) => {
|
test('Parts - 404', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/99999/' });
|
||||||
|
|
||||||
await navigate(page, 'part/99999/');
|
|
||||||
await page.getByText('Page Not Found', { exact: true }).waitFor();
|
await page.getByText('Page Not Found', { exact: true }).waitFor();
|
||||||
|
|
||||||
// Clear out any console error messages
|
// Clear out any console error messages
|
||||||
await page.evaluate(() => console.clear());
|
await page.evaluate(() => console.clear());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Revision', async ({ page }) => {
|
test('Parts - Revision', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'part/906/details' });
|
||||||
|
|
||||||
await navigate(page, 'part/906/details');
|
|
||||||
|
|
||||||
await page.getByText('Revision of').waitFor();
|
await page.getByText('Revision of').waitFor();
|
||||||
await page.getByText('Select Part Revision').waitFor();
|
await page.getByText('Select Part Revision').waitFor();
|
||||||
@ -421,10 +392,10 @@ test('Parts - Revision', async ({ page }) => {
|
|||||||
await page.getByText('Select Part Revision').waitFor();
|
await page.getByText('Select Part Revision').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parts - Bulk Edit', async ({ page }) => {
|
test('Parts - Bulk Edit', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, {
|
||||||
|
url: 'part/category/index/parts'
|
||||||
await navigate(page, 'part/category/index/parts');
|
});
|
||||||
|
|
||||||
// Edit the category for multiple parts
|
// Edit the category for multiple parts
|
||||||
await page.getByLabel('Select record 1', { exact: true }).click();
|
await page.getByLabel('Select record 1', { exact: true }).click();
|
||||||
|
@ -11,12 +11,14 @@ import {
|
|||||||
openFilterDrawer,
|
openFilterDrawer,
|
||||||
setTableChoiceFilter
|
setTableChoiceFilter
|
||||||
} from '../helpers.ts';
|
} from '../helpers.ts';
|
||||||
import { doQuickLogin } from '../login.ts';
|
import { doCachedLogin } from '../login.ts';
|
||||||
|
|
||||||
test('Purchase Orders - Table', async ({ page }) => {
|
test('Purchase Orders - Table', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
|
|
||||||
await loadTab(page, 'Purchase Orders');
|
await loadTab(page, 'Purchase Orders');
|
||||||
await activateTableView(page);
|
await activateTableView(page);
|
||||||
|
|
||||||
@ -42,10 +44,11 @@ test('Purchase Orders - Table', async ({ page }) => {
|
|||||||
await page.getByText('2025-07-17').waitFor(); // Target Date
|
await page.getByText('2025-07-17').waitFor(); // Target Date
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - Calendar', async ({ page }) => {
|
test('Purchase Orders - Calendar', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
await loadTab(page, 'Purchase Orders');
|
await loadTab(page, 'Purchase Orders');
|
||||||
|
|
||||||
// Ensure view is in "calendar" mode
|
// Ensure view is in "calendar" mode
|
||||||
@ -66,10 +69,11 @@ test('Purchase Orders - Calendar', async ({ page }) => {
|
|||||||
await activateTableView(page);
|
await activateTableView(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - Barcodes', async ({ page }) => {
|
test('Purchase Orders - Barcodes', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, {
|
||||||
|
url: 'purchasing/purchase-order/13/detail'
|
||||||
|
});
|
||||||
|
|
||||||
await navigate(page, 'purchasing/purchase-order/13/detail');
|
|
||||||
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
|
|
||||||
// Display QR code
|
// Display QR code
|
||||||
@ -126,10 +130,11 @@ test('Purchase Orders - Barcodes', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - General', async ({ page }) => {
|
test('Purchase Orders - General', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
|
|
||||||
await page.getByRole('cell', { name: 'PO0012' }).click();
|
await page.getByRole('cell', { name: 'PO0012' }).click();
|
||||||
await page.waitForTimeout(200);
|
await page.waitForTimeout(200);
|
||||||
@ -179,10 +184,15 @@ test('Purchase Orders - General', async ({ page }) => {
|
|||||||
await page.getByRole('tab', { name: 'Details' }).waitFor();
|
await page.getByRole('tab', { name: 'Details' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - Filters', async ({ page }) => {
|
test('Purchase Orders - Filters', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'reader', 'readonly');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'reader',
|
||||||
|
password: 'readonly'
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
|
|
||||||
await loadTab(page, 'Purchase Orders');
|
await loadTab(page, 'Purchase Orders');
|
||||||
await activateTableView(page);
|
await activateTableView(page);
|
||||||
|
|
||||||
@ -204,11 +214,13 @@ test('Purchase Orders - Filters', async ({ page }) => {
|
|||||||
await page.getByRole('option', { name: 'Target Date After' }).waitFor();
|
await page.getByRole('option', { name: 'Target Date After' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - Order Parts', async ({ page }) => {
|
test('Purchase Orders - Order Parts', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// Open "Order Parts" wizard from the "parts" table
|
// Open "Order Parts" wizard from the "parts" table
|
||||||
await page.getByRole('tab', { name: 'Parts' }).click();
|
await page.getByRole('tab', { name: 'Parts' }).click();
|
||||||
|
await page.waitForURL('**/part/category/index/**');
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByLabel('panel-tabs-partcategory')
|
.getByLabel('panel-tabs-partcategory')
|
||||||
.getByRole('tab', { name: 'Parts' })
|
.getByRole('tab', { name: 'Parts' })
|
||||||
@ -284,10 +296,12 @@ test('Purchase Orders - Order Parts', async ({ page }) => {
|
|||||||
/**
|
/**
|
||||||
* Tests for receiving items against a purchase order
|
* Tests for receiving items against a purchase order
|
||||||
*/
|
*/
|
||||||
test('Purchase Orders - Receive Items', async ({ page }) => {
|
test('Purchase Orders - Receive Items', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
|
|
||||||
await page.getByRole('cell', { name: 'PO0014' }).click();
|
await page.getByRole('cell', { name: 'PO0014' }).click();
|
||||||
|
|
||||||
await loadTab(page, 'Order Details');
|
await loadTab(page, 'Order Details');
|
||||||
@ -351,10 +365,11 @@ test('Purchase Orders - Receive Items', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'bucket' }).first().waitFor();
|
await page.getByRole('cell', { name: 'bucket' }).first().waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Purchase Orders - Duplicate', async ({ page }) => {
|
test('Purchase Orders - Duplicate', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, {
|
||||||
|
url: 'purchasing/purchase-order/13/detail'
|
||||||
|
});
|
||||||
|
|
||||||
await navigate(page, 'purchasing/purchase-order/13/detail');
|
|
||||||
await page.getByLabel('action-menu-order-actions').click();
|
await page.getByLabel('action-menu-order-actions').click();
|
||||||
await page.getByLabel('action-menu-order-actions-duplicate').click();
|
await page.getByLabel('action-menu-order-actions-duplicate').click();
|
||||||
|
|
||||||
|
@ -4,15 +4,13 @@ import {
|
|||||||
clearTableFilters,
|
clearTableFilters,
|
||||||
globalSearch,
|
globalSearch,
|
||||||
loadTab,
|
loadTab,
|
||||||
navigate,
|
|
||||||
setTableChoiceFilter
|
setTableChoiceFilter
|
||||||
} from '../helpers.ts';
|
} from '../helpers.ts';
|
||||||
import { doQuickLogin } from '../login.ts';
|
import { doCachedLogin } from '../login.ts';
|
||||||
|
|
||||||
test('Sales Orders - Tabs', async ({ page }) => {
|
test('Sales Orders - Tabs', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'sales/index/' });
|
||||||
|
|
||||||
await navigate(page, 'sales/index/');
|
|
||||||
await page.waitForURL('**/web/sales/**');
|
await page.waitForURL('**/web/sales/**');
|
||||||
|
|
||||||
await loadTab(page, 'Sales Orders');
|
await loadTab(page, 'Sales Orders');
|
||||||
@ -63,10 +61,12 @@ test('Sales Orders - Tabs', async ({ page }) => {
|
|||||||
await loadTab(page, 'Notes');
|
await loadTab(page, 'Notes');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Sales Orders - Basic Tests', async ({ page }) => {
|
test('Sales Orders - Basic Tests', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Sales' }).click();
|
await page.getByRole('tab', { name: 'Sales' }).click();
|
||||||
|
await page.waitForURL('**/sales/index/**');
|
||||||
|
|
||||||
await loadTab(page, 'Sales Orders');
|
await loadTab(page, 'Sales Orders');
|
||||||
|
|
||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
@ -102,10 +102,12 @@ test('Sales Orders - Basic Tests', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Sales Orders - Shipments', async ({ page }) => {
|
test('Sales Orders - Shipments', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Sales' }).click();
|
await page.getByRole('tab', { name: 'Sales' }).click();
|
||||||
|
await page.waitForURL('**/sales/index/**');
|
||||||
|
|
||||||
await loadTab(page, 'Sales Orders');
|
await loadTab(page, 'Sales Orders');
|
||||||
|
|
||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
@ -131,7 +133,7 @@ test('Sales Orders - Shipments', async ({ page }) => {
|
|||||||
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
||||||
|
|
||||||
// Ensure the form has loaded
|
// Ensure the form has loaded
|
||||||
await page.waitForTimeout(500);
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
let tracking_number = await page
|
let tracking_number = await page
|
||||||
.getByLabel('text-field-tracking_number')
|
.getByLabel('text-field-tracking_number')
|
||||||
@ -201,10 +203,11 @@ test('Sales Orders - Shipments', async ({ page }) => {
|
|||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Sales Orders - Duplicate', async ({ page }) => {
|
test('Sales Orders - Duplicate', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, {
|
||||||
|
url: 'sales/sales-order/11/detail'
|
||||||
|
});
|
||||||
|
|
||||||
await navigate(page, 'sales/sales-order/11/detail');
|
|
||||||
await page.getByLabel('action-menu-order-actions').click();
|
await page.getByLabel('action-menu-order-actions').click();
|
||||||
await page.getByLabel('action-menu-order-actions-duplicate').click();
|
await page.getByLabel('action-menu-order-actions-duplicate').click();
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { test } from '../baseFixtures';
|
import { test } from '../baseFixtures';
|
||||||
import { navigate } from '../helpers';
|
import { doCachedLogin } from '../login';
|
||||||
import { doQuickLogin } from '../login';
|
|
||||||
|
|
||||||
const scan = async (page, barcode) => {
|
const scan = async (page, barcode) => {
|
||||||
await page.getByLabel('barcode-input-scanner').click();
|
await page.getByLabel('barcode-input-scanner').click();
|
||||||
@ -8,8 +7,8 @@ const scan = async (page, barcode) => {
|
|||||||
await page.getByRole('button', { name: 'Scan', exact: true }).click();
|
await page.getByRole('button', { name: 'Scan', exact: true }).click();
|
||||||
};
|
};
|
||||||
|
|
||||||
test('Scanning - Dialog', async ({ page }) => {
|
test('Scanning - Dialog', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Open Barcode Scanner' }).click();
|
await page.getByRole('button', { name: 'Open Barcode Scanner' }).click();
|
||||||
await scan(page, '{"part": 15}');
|
await scan(page, '{"part": 15}');
|
||||||
@ -19,8 +18,8 @@ test('Scanning - Dialog', async ({ page }) => {
|
|||||||
await page.getByText('Required:').waitFor();
|
await page.getByText('Required:').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - Basic', async ({ page }) => {
|
test('Scanning - Basic', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// Navigate to the 'scan' page
|
// Navigate to the 'scan' page
|
||||||
await page.getByLabel('navigation-menu').click();
|
await page.getByLabel('navigation-menu').click();
|
||||||
@ -40,9 +39,8 @@ test('Scanning - Basic', async ({ page }) => {
|
|||||||
await page.getByText('No match found for barcode').waitFor();
|
await page.getByText('No match found for barcode').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - Part', async ({ page }) => {
|
test('Scanning - Part', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
|
|
||||||
await scan(page, '{"part": 1}');
|
await scan(page, '{"part": 1}');
|
||||||
|
|
||||||
@ -51,9 +49,8 @@ test('Scanning - Part', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'part', exact: true }).waitFor();
|
await page.getByRole('cell', { name: 'part', exact: true }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - Stockitem', async ({ page }) => {
|
test('Scanning - Stockitem', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"stockitem": 408}');
|
await scan(page, '{"stockitem": 408}');
|
||||||
|
|
||||||
await page.getByText('1551ABK').waitFor();
|
await page.getByText('1551ABK').waitFor();
|
||||||
@ -61,9 +58,9 @@ test('Scanning - Stockitem', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Quantity: 100' }).waitFor();
|
await page.getByRole('cell', { name: 'Quantity: 100' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - StockLocation', async ({ page }) => {
|
test('Scanning - StockLocation', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"stocklocation": 3}');
|
await scan(page, '{"stocklocation": 3}');
|
||||||
|
|
||||||
// stocklocation: 3
|
// stocklocation: 3
|
||||||
@ -74,20 +71,17 @@ test('Scanning - StockLocation', async ({ page }) => {
|
|||||||
.waitFor();
|
.waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - SupplierPart', async ({ page }) => {
|
test('Scanning - SupplierPart', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"supplierpart": 204}');
|
await scan(page, '{"supplierpart": 204}');
|
||||||
|
|
||||||
// supplierpart: 204
|
await page.waitForLoadState('networkidle');
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
await page.getByText('1551ABK').first().waitFor();
|
await page.getByText('1551ABK').first().waitFor();
|
||||||
await page.getByRole('cell', { name: 'supplierpart', exact: true }).waitFor();
|
await page.getByRole('cell', { name: 'supplierpart', exact: true }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - PurchaseOrder', async ({ page }) => {
|
test('Scanning - PurchaseOrder', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"purchaseorder": 12}');
|
await scan(page, '{"purchaseorder": 12}');
|
||||||
|
|
||||||
// purchaseorder: 12
|
// purchaseorder: 12
|
||||||
@ -98,9 +92,9 @@ test('Scanning - PurchaseOrder', async ({ page }) => {
|
|||||||
.waitFor();
|
.waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - SalesOrder', async ({ page }) => {
|
test('Scanning - SalesOrder', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"salesorder": 6}');
|
await scan(page, '{"salesorder": 6}');
|
||||||
|
|
||||||
// salesorder: 6
|
// salesorder: 6
|
||||||
@ -109,9 +103,8 @@ test('Scanning - SalesOrder', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'salesorder', exact: true }).waitFor();
|
await page.getByRole('cell', { name: 'salesorder', exact: true }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Scanning - Build', async ({ page }) => {
|
test('Scanning - Build', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'scan/' });
|
||||||
await navigate(page, 'scan/');
|
|
||||||
await scan(page, '{"build": 8}');
|
await scan(page, '{"build": 8}');
|
||||||
|
|
||||||
// build: 8
|
// build: 8
|
||||||
|
@ -7,12 +7,11 @@ import {
|
|||||||
openFilterDrawer,
|
openFilterDrawer,
|
||||||
setTableChoiceFilter
|
setTableChoiceFilter
|
||||||
} from '../helpers.js';
|
} from '../helpers.js';
|
||||||
import { doQuickLogin } from '../login.js';
|
import { doCachedLogin } from '../login.js';
|
||||||
|
|
||||||
test('Stock - Basic Tests', async ({ page }) => {
|
test('Stock - Basic Tests', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
|
||||||
await page.waitForURL('**/web/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
await loadTab(page, 'Location Details');
|
await loadTab(page, 'Location Details');
|
||||||
@ -39,10 +38,9 @@ test('Stock - Basic Tests', async ({ page }) => {
|
|||||||
await loadTab(page, 'Installed Items');
|
await loadTab(page, 'Installed Items');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stock - Location Tree', async ({ page }) => {
|
test('Stock - Location Tree', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
|
||||||
await page.waitForURL('**/web/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
await loadTab(page, 'Location Details');
|
await loadTab(page, 'Location Details');
|
||||||
|
|
||||||
@ -56,10 +54,13 @@ test('Stock - Location Tree', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Factory' }).first().waitFor();
|
await page.getByRole('cell', { name: 'Factory' }).first().waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stock - Filters', async ({ page }) => {
|
test('Stock - Filters', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
|
password: 'wizardstaff',
|
||||||
|
url: '/stock/location/index/'
|
||||||
|
});
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
|
||||||
await loadTab(page, 'Stock Items');
|
await loadTab(page, 'Stock Items');
|
||||||
|
|
||||||
await openFilterDrawer(page);
|
await openFilterDrawer(page);
|
||||||
@ -101,8 +102,8 @@ test('Stock - Filters', async ({ page }) => {
|
|||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stock - Serial Numbers', async ({ page }) => {
|
test('Stock - Serial Numbers', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
// Use the "global search" functionality to find a part we are interested in
|
// Use the "global search" functionality to find a part we are interested in
|
||||||
// This is to exercise the search functionality and ensure it is working as expected
|
// This is to exercise the search functionality and ensure it is working as expected
|
||||||
@ -167,10 +168,8 @@ test('Stock - Serial Numbers', async ({ page }) => {
|
|||||||
/**
|
/**
|
||||||
* Test various 'actions' on the stock detail page
|
* Test various 'actions' on the stock detail page
|
||||||
*/
|
*/
|
||||||
test('Stock - Stock Actions', async ({ page }) => {
|
test('Stock - Stock Actions', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/item/1225/details' });
|
||||||
|
|
||||||
await navigate(page, 'stock/item/1225/details');
|
|
||||||
|
|
||||||
// Helper function to launch a stock action
|
// Helper function to launch a stock action
|
||||||
const launchStockAction = async (action: string) => {
|
const launchStockAction = async (action: string) => {
|
||||||
@ -231,11 +230,9 @@ test('Stock - Stock Actions', async ({ page }) => {
|
|||||||
await page.getByLabel('action-menu-stock-operations-return').click();
|
await page.getByLabel('action-menu-stock-operations-return').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stock - Tracking', async ({ page }) => {
|
test('Stock - Tracking', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/item/176/details' });
|
||||||
|
|
||||||
// Navigate to the "stock item" page
|
|
||||||
await navigate(page, 'stock/item/176/details/');
|
|
||||||
await page.getByRole('link', { name: 'Widget Assembly # 2' }).waitFor();
|
await page.getByRole('link', { name: 'Widget Assembly # 2' }).waitFor();
|
||||||
|
|
||||||
// Navigate to the "stock tracking" tab
|
// Navigate to the "stock tracking" tab
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import test from '@playwright/test';
|
import test from '@playwright/test';
|
||||||
import { globalSearch, loadTab, navigate } from './helpers';
|
import { globalSearch, loadTab, navigate } from './helpers';
|
||||||
import { doQuickLogin } from './login';
|
import { doCachedLogin } from './login';
|
||||||
|
|
||||||
// Helper function to open the export data dialog
|
// Helper function to open the export data dialog
|
||||||
const openExportDialog = async (page) => {
|
const openExportDialog = async (page) => {
|
||||||
@ -11,11 +11,12 @@ const openExportDialog = async (page) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Test data export for various order types
|
// Test data export for various order types
|
||||||
test('Exporting - Orders', async ({ page }) => {
|
test('Exporting - Orders', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
// Download list of purchase orders
|
password: 'wizardstaff',
|
||||||
await navigate(page, 'purchasing/index/purchase-orders');
|
url: 'purchasing/index/purchase-orders'
|
||||||
|
});
|
||||||
|
|
||||||
await openExportDialog(page);
|
await openExportDialog(page);
|
||||||
|
|
||||||
@ -69,8 +70,11 @@ test('Exporting - Orders', async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test for custom BOM exporter
|
// Test for custom BOM exporter
|
||||||
test('Exporting - BOM', async ({ page }) => {
|
test('Exporting - BOM', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
|
password: 'wizardstaff'
|
||||||
|
});
|
||||||
|
|
||||||
await globalSearch(page, 'MAST');
|
await globalSearch(page, 'MAST');
|
||||||
await page.getByLabel('search-group-results-part').locator('a').click();
|
await page.getByLabel('search-group-results-part').locator('a').click();
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
/** Unit tests for form validation, rendering, etc */
|
/** Unit tests for form validation, rendering, etc */
|
||||||
import test from 'playwright/test';
|
import test from 'playwright/test';
|
||||||
import { navigate } from './helpers';
|
import { navigate } from './helpers';
|
||||||
import { doQuickLogin } from './login';
|
import { doCachedLogin } from './login';
|
||||||
|
|
||||||
|
test('Forms - Stock Item Validation', async ({ browser }) => {
|
||||||
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
|
password: 'wizardstaff',
|
||||||
|
url: 'stock/location/index/stock-items'
|
||||||
|
});
|
||||||
|
|
||||||
test('Forms - Stock Item Validation', async ({ page }) => {
|
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
|
||||||
await navigate(page, 'stock/location/index/stock-items');
|
|
||||||
await page.waitForURL('**/web/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
// Create new stock item form
|
// Create new stock item form
|
||||||
@ -74,9 +78,12 @@ test('Forms - Stock Item Validation', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Electronics Lab' }).waitFor();
|
await page.getByRole('cell', { name: 'Electronics Lab' }).waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Forms - Supplier Validation', async ({ page, request }) => {
|
test('Forms - Supplier Validation', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
const page = await doCachedLogin(browser, {
|
||||||
await navigate(page, 'purchasing/index/suppliers');
|
username: 'steven',
|
||||||
|
password: 'wizardstaff',
|
||||||
|
url: 'purchasing/index/suppliers'
|
||||||
|
});
|
||||||
await page.waitForURL('**/purchasing/index/**');
|
await page.waitForURL('**/purchasing/index/**');
|
||||||
|
|
||||||
await page.getByLabel('action-button-add-company').click();
|
await page.getByLabel('action-button-add-company').click();
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { test } from './baseFixtures.js';
|
import { test } from './baseFixtures.js';
|
||||||
import { globalSearch, navigate } from './helpers.js';
|
import { globalSearch } from './helpers.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for integration of django admin button
|
* Test for integration of django admin button
|
||||||
*/
|
*/
|
||||||
test('Admin Button', async ({ page }) => {
|
test('Admin Button', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
await navigate(page, 'company/1/details');
|
username: 'admin',
|
||||||
|
password: 'inventree',
|
||||||
|
url: 'company/1/details'
|
||||||
|
});
|
||||||
|
|
||||||
// Click on the admin button
|
// Click on the admin button
|
||||||
await page.getByLabel(/action-button-open-in-admin/).click();
|
await page.getByLabel(/action-button-open-in-admin/).click();
|
||||||
@ -18,8 +21,11 @@ test('Admin Button', async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Tests for the global search functionality
|
// Tests for the global search functionality
|
||||||
test('Search', async ({ page }) => {
|
test('Search', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'steven',
|
||||||
|
password: 'wizardstaff'
|
||||||
|
});
|
||||||
|
|
||||||
await globalSearch(page, 'another customer');
|
await globalSearch(page, 'another customer');
|
||||||
|
|
||||||
|
@ -1,41 +1,7 @@
|
|||||||
import { expect, test } from './baseFixtures.js';
|
import { expect, test } from './baseFixtures.js';
|
||||||
import { logoutUrl, user } from './defaults.js';
|
import { logoutUrl } from './defaults.js';
|
||||||
import { navigate } from './helpers.js';
|
import { navigate } from './helpers.js';
|
||||||
import { doLogin, doQuickLogin } from './login.js';
|
import { doLogin } from './login.js';
|
||||||
|
|
||||||
test('Login - Basic Test', async ({ page }) => {
|
|
||||||
await doLogin(page);
|
|
||||||
|
|
||||||
// Check that the username is provided
|
|
||||||
await page.getByText(user.username);
|
|
||||||
|
|
||||||
// Logout (via menu)
|
|
||||||
await page.getByRole('button', { name: 'Ally Access' }).click();
|
|
||||||
await page.getByRole('menuitem', { name: 'Logout' }).click();
|
|
||||||
|
|
||||||
await page.waitForURL('**/web/login');
|
|
||||||
await page.getByLabel('username');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Login - Quick Test', async ({ page }) => {
|
|
||||||
await doQuickLogin(page);
|
|
||||||
|
|
||||||
// Check that the username is provided
|
|
||||||
await page.getByText(user.username);
|
|
||||||
|
|
||||||
await expect(page).toHaveTitle(/^InvenTree/);
|
|
||||||
|
|
||||||
// Go to the dashboard
|
|
||||||
await navigate(page, '');
|
|
||||||
await page.waitForURL('**/web');
|
|
||||||
|
|
||||||
await page.getByText('InvenTree Demo Server - ').waitFor();
|
|
||||||
|
|
||||||
// Logout (via URL)
|
|
||||||
await navigate(page, 'logout');
|
|
||||||
await page.waitForURL('**/web/login');
|
|
||||||
await page.getByLabel('username');
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test various types of login failure
|
* Test various types of login failure
|
||||||
@ -79,7 +45,7 @@ test('Login - Failures', async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Login - Change Password', async ({ page }) => {
|
test('Login - Change Password', async ({ page }) => {
|
||||||
await doQuickLogin(page, 'noaccess', 'youshallnotpass');
|
await doLogin(page, 'noaccess', 'youshallnotpass');
|
||||||
|
|
||||||
// Navigate to the 'change password' page
|
// Navigate to the 'change password' page
|
||||||
await navigate(page, 'settings/user/account');
|
await navigate(page, 'settings/user/account');
|
||||||
@ -105,6 +71,4 @@ test('Login - Change Password', async ({ page }) => {
|
|||||||
|
|
||||||
await page.getByText('Password Changed').waitFor();
|
await page.getByText('Password Changed').waitFor();
|
||||||
await page.getByText('The password was set successfully').waitFor();
|
await page.getByText('The password was set successfully').waitFor();
|
||||||
|
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,61 @@
|
|||||||
import { systemKey, test } from './baseFixtures.js';
|
import { systemKey, test } from './baseFixtures.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
|
|
||||||
test('Quick Command', async ({ page }) => {
|
test('Modals - Admin', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
|
// use server info
|
||||||
|
await page.getByLabel('open-spotlight').click();
|
||||||
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'Server Information About this InvenTree instance'
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
||||||
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
|
// use license info
|
||||||
|
await page.getByLabel('open-spotlight').click();
|
||||||
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'License Information Licenses for dependencies of the service'
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
await page.getByText('License Information').first().waitFor();
|
||||||
|
await page.getByRole('tab', { name: 'backend Packages' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Babel BSD License' }).click();
|
||||||
|
await page
|
||||||
|
.getByText('by the Babel Team, see AUTHORS for more information')
|
||||||
|
.waitFor();
|
||||||
|
|
||||||
|
await page.getByRole('tab', { name: 'frontend Packages' }).click();
|
||||||
|
await page.getByRole('button', { name: '@sentry/core MIT' }).click();
|
||||||
|
await page
|
||||||
|
.getByLabel('@sentry/coreMIT')
|
||||||
|
.getByText('Copyright (c) 2019')
|
||||||
|
.waitFor();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByLabel('License Information')
|
||||||
|
.getByRole('button')
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// use about
|
||||||
|
await page.getByLabel('open-spotlight').click();
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'About InvenTree About the InvenTree org' })
|
||||||
|
.click();
|
||||||
|
await page.getByRole('cell', { name: 'InvenTree Version' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Quick Command', async ({ browser }) => {
|
||||||
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Open Spotlight with Keyboard Shortcut and Search
|
// Open Spotlight with Keyboard Shortcut and Search
|
||||||
await page.locator('body').press(`${systemKey}+k`);
|
await page.locator('body').press(`${systemKey}+k`);
|
||||||
@ -10,11 +63,12 @@ test('Quick Command', async ({ page }) => {
|
|||||||
await page.getByPlaceholder('Search...').fill('Dashboard');
|
await page.getByPlaceholder('Search...').fill('Dashboard');
|
||||||
await page.getByPlaceholder('Search...').press('Tab');
|
await page.getByPlaceholder('Search...').press('Tab');
|
||||||
await page.getByPlaceholder('Search...').press('Enter');
|
await page.getByPlaceholder('Search...').press('Enter');
|
||||||
await page.waitForURL('**/web/home');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Quick Command - No Keys', async ({ page }) => {
|
test('Quick Command - No Keys', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Open Spotlight with Button
|
// Open Spotlight with Button
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
@ -23,15 +77,15 @@ test('Quick Command - No Keys', async ({ page }) => {
|
|||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page.getByText('InvenTree Demo Server - ').waitFor();
|
await page.getByText('InvenTree Demo Server - ').waitFor();
|
||||||
await page.waitForURL('**/web/home');
|
|
||||||
|
|
||||||
// Use navigation menu
|
// Use navigation menu
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'Open Navigation Open the main' })
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Open Navigation' }).click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(250);
|
||||||
|
|
||||||
// assert the nav headers are visible
|
// assert the nav headers are visible
|
||||||
await page.getByText('Navigation').waitFor();
|
await page.getByText('Navigation').waitFor();
|
||||||
@ -55,8 +109,6 @@ test('Quick Command - No Keys', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
||||||
await page.getByRole('button', { name: 'Close' }).click();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
await page.waitForURL('**/web/home');
|
|
||||||
|
|
||||||
// use license info
|
// use license info
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
await page
|
await page
|
@ -6,12 +6,15 @@ import {
|
|||||||
loadTab,
|
loadTab,
|
||||||
navigate
|
navigate
|
||||||
} from './helpers.js';
|
} from './helpers.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
import { setPluginState, setSettingState } from './settings.js';
|
import { setPluginState, setSettingState } from './settings.js';
|
||||||
|
|
||||||
// Unit test for plugin settings
|
// Unit test for plugin settings
|
||||||
test('Plugins - Settings', async ({ page, request }) => {
|
test('Plugins - Settings', async ({ browser, request }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure that the SampleIntegration plugin is enabled
|
// Ensure that the SampleIntegration plugin is enabled
|
||||||
await setPluginState({
|
await setPluginState({
|
||||||
@ -57,11 +60,14 @@ test('Plugins - Settings', async ({ page, request }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test base plugin functionality
|
// Test base plugin functionality
|
||||||
test('Plugins - Functionality', async ({ page, request }) => {
|
test('Plugins - Functionality', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
|
||||||
|
|
||||||
// Navigate and select the plugin
|
// Navigate and select the plugin
|
||||||
await navigate(page, 'settings/admin/plugin/');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree',
|
||||||
|
url: 'settings/admin/plugin/'
|
||||||
|
});
|
||||||
|
|
||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
await page.getByPlaceholder('Search').fill('sample');
|
await page.getByPlaceholder('Search').fill('sample');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
@ -80,8 +86,11 @@ test('Plugins - Functionality', async ({ page, request }) => {
|
|||||||
await page.getByText('The plugin was deactivated').waitFor();
|
await page.getByText('The plugin was deactivated').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Plugins - Panels', async ({ page, request }) => {
|
test('Plugins - Panels', async ({ browser, request }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure that UI plugins are enabled
|
// Ensure that UI plugins are enabled
|
||||||
await setSettingState({
|
await setSettingState({
|
||||||
@ -108,7 +117,7 @@ test('Plugins - Panels', async ({ page, request }) => {
|
|||||||
await loadTab(page, 'Part Details');
|
await loadTab(page, 'Part Details');
|
||||||
|
|
||||||
// Allow time for the plugin panels to load (they are loaded asynchronously)
|
// Allow time for the plugin panels to load (they are loaded asynchronously)
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Check out each of the plugin panels
|
// Check out each of the plugin panels
|
||||||
await loadTab(page, 'Broken Panel');
|
await loadTab(page, 'Broken Panel');
|
||||||
@ -139,8 +148,11 @@ test('Plugins - Panels', async ({ page, request }) => {
|
|||||||
/**
|
/**
|
||||||
* Unit test for custom admin integration for plugins
|
* Unit test for custom admin integration for plugins
|
||||||
*/
|
*/
|
||||||
test('Plugins - Custom Admin', async ({ page, request }) => {
|
test('Plugins - Custom Admin', async ({ browser, request }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure that the SampleUI plugin is enabled
|
// Ensure that the SampleUI plugin is enabled
|
||||||
await setPluginState({
|
await setPluginState({
|
||||||
@ -170,8 +182,11 @@ test('Plugins - Custom Admin', async ({ page, request }) => {
|
|||||||
await page.getByText('hello: world').waitFor();
|
await page.getByText('hello: world').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Plugins - Locate Item', async ({ page, request }) => {
|
test('Plugins - Locate Item', async ({ browser, request }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure that the sample location plugin is enabled
|
// Ensure that the sample location plugin is enabled
|
||||||
await setPluginState({
|
await setPluginState({
|
||||||
@ -184,6 +199,7 @@ test('Plugins - Locate Item', async ({ page, request }) => {
|
|||||||
|
|
||||||
// Navigate to the "stock item" page
|
// Navigate to the "stock item" page
|
||||||
await navigate(page, 'stock/item/287/');
|
await navigate(page, 'stock/item/287/');
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// "Locate" this item
|
// "Locate" this item
|
||||||
await page.getByLabel('action-button-locate-item').click();
|
await page.getByLabel('action-button-locate-item').click();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { expect, test } from './baseFixtures.js';
|
import { expect, test } from './baseFixtures.js';
|
||||||
import { activateTableView, loadTab, navigate } from './helpers.js';
|
import { activateTableView, loadTab } from './helpers.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
import { setPluginState } from './settings.js';
|
import { setPluginState } from './settings.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -8,10 +8,9 @@ import { setPluginState } from './settings.js';
|
|||||||
* Select a number of stock items from the table,
|
* Select a number of stock items from the table,
|
||||||
* and print labels against them
|
* and print labels against them
|
||||||
*/
|
*/
|
||||||
test('Label Printing', async ({ page }) => {
|
test('Label Printing', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
|
||||||
await page.waitForURL('**/web/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
await loadTab(page, 'Stock Items');
|
await loadTab(page, 'Stock Items');
|
||||||
@ -50,10 +49,9 @@ test('Label Printing', async ({ page }) => {
|
|||||||
* Navigate to a PurchaseOrder detail page,
|
* Navigate to a PurchaseOrder detail page,
|
||||||
* and print a report against it.
|
* and print a report against it.
|
||||||
*/
|
*/
|
||||||
test('Report Printing', async ({ page }) => {
|
test('Report Printing', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
|
||||||
await page.waitForURL('**/web/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
// Navigate to a specific PurchaseOrder
|
// Navigate to a specific PurchaseOrder
|
||||||
@ -81,9 +79,11 @@ test('Report Printing', async ({ page }) => {
|
|||||||
await page.context().close();
|
await page.context().close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Report Editing', async ({ page, request }) => {
|
test('Report Editing', async ({ browser, request }) => {
|
||||||
const [username, password] = ['admin', 'inventree'];
|
const page = await doCachedLogin(browser, {
|
||||||
await doQuickLogin(page, username, password);
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// activate the sample plugin for this test
|
// activate the sample plugin for this test
|
||||||
await setPluginState({
|
await setPluginState({
|
||||||
|
@ -1,49 +1,45 @@
|
|||||||
import { expect, test } from './baseFixtures.js';
|
import { expect, test } from './baseFixtures.js';
|
||||||
import { apiUrl } from './defaults.js';
|
import { apiUrl } from './defaults.js';
|
||||||
import { getRowFromCell, loadTab, navigate } from './helpers.js';
|
import { getRowFromCell, loadTab, navigate } from './helpers.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
import { setSettingState } from './settings.js';
|
import { setSettingState } from './settings.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust language and color settings
|
* Adjust language and color settings
|
||||||
|
*
|
||||||
|
* TODO: Reimplement this - without logging out a cached user
|
||||||
*/
|
*/
|
||||||
test('Settings - Language / Color', async ({ page }) => {
|
// test('Settings - Language / Color', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
// const page = await doCachedLogin(browser);
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Ally Access' }).click();
|
// await page.getByRole('button', { name: 'Ally Access' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Logout' }).click();
|
// await page.getByRole('menuitem', { name: 'Logout' }).click();
|
||||||
await page.getByRole('button', { name: 'Send me an email' }).click();
|
// await page.getByRole('button', { name: 'Send me an email' }).click();
|
||||||
await page.getByLabel('Language toggle').click();
|
// await page.getByLabel('Language toggle').click();
|
||||||
await page.getByLabel('Select language').first().click();
|
// await page.getByLabel('Select language').first().click();
|
||||||
await page.getByRole('option', { name: 'German' }).click();
|
// await page.getByRole('option', { name: 'German' }).click();
|
||||||
await page.waitForTimeout(200);
|
// await page.waitForTimeout(200);
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Benutzername und Passwort' }).click();
|
// await page.getByRole('button', { name: 'Benutzername und Passwort' }).click();
|
||||||
await page.getByPlaceholder('Ihr Benutzername').click();
|
// await page.getByPlaceholder('Ihr Benutzername').click();
|
||||||
await page.getByPlaceholder('Ihr Benutzername').fill('admin');
|
// await page.getByPlaceholder('Ihr Benutzername').fill('admin');
|
||||||
await page.getByPlaceholder('Ihr Benutzername').press('Tab');
|
// await page.getByPlaceholder('Ihr Benutzername').press('Tab');
|
||||||
await page.getByPlaceholder('Dein Passwort').fill('inventree');
|
// await page.getByPlaceholder('Dein Passwort').fill('inventree');
|
||||||
await page.getByRole('button', { name: 'Anmelden' }).click();
|
// await page.getByRole('button', { name: 'Anmelden' }).click();
|
||||||
await page.waitForTimeout(200);
|
// await page.waitForTimeout(200);
|
||||||
|
|
||||||
// Note: changes to the dashboard have invalidated these tests (for now)
|
// await page.getByRole('tab', { name: 'Dashboard' }).click();
|
||||||
// await page
|
// await page.waitForURL('**/web/home');
|
||||||
// .locator('span')
|
// });
|
||||||
// .filter({ hasText: 'AnzeigeneinstellungenFarbmodusSprache' })
|
|
||||||
// .getByRole('button')
|
|
||||||
// .click();
|
|
||||||
// await page
|
|
||||||
// .locator('span')
|
|
||||||
// .filter({ hasText: 'AnzeigeneinstellungenFarbmodusSprache' })
|
|
||||||
// .getByRole('button')
|
|
||||||
// .click();
|
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Dashboard' }).click();
|
test('Settings - User theme', async ({ browser }) => {
|
||||||
await page.waitForURL('**/web/home');
|
const page = await doCachedLogin(browser, {
|
||||||
});
|
username: 'allaccess',
|
||||||
|
password: 'nolimits'
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
test('Settings - User theme', async ({ page }) => {
|
|
||||||
await doQuickLogin(page);
|
|
||||||
await page.getByRole('button', { name: 'Ally Access' }).click();
|
await page.getByRole('button', { name: 'Ally Access' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Account settings' }).click();
|
await page.getByRole('menuitem', { name: 'Account settings' }).click();
|
||||||
|
|
||||||
@ -82,14 +78,14 @@ test('Settings - User theme', async ({ page }) => {
|
|||||||
// primary
|
// primary
|
||||||
await page.getByLabel('#fab005').click();
|
await page.getByLabel('#fab005').click();
|
||||||
await page.getByLabel('#228be6').click();
|
await page.getByLabel('#228be6').click();
|
||||||
|
|
||||||
// language
|
|
||||||
await page.getByRole('button', { name: 'Use pseudo language' }).click();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings - Admin', async ({ page }) => {
|
test('Settings - Admin', async ({ browser }) => {
|
||||||
// Note here we login with admin access
|
// Note here we login with admin access
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// User settings
|
// User settings
|
||||||
await page.getByRole('button', { name: 'admin' }).click();
|
await page.getByRole('button', { name: 'admin' }).click();
|
||||||
@ -184,9 +180,12 @@ test('Settings - Admin', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Submit' }).click();
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings - Admin - Barcode History', async ({ page, request }) => {
|
test('Settings - Admin - Barcode History', async ({ browser, request }) => {
|
||||||
// Login with admin credentials
|
// Login with admin credentials
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure that the "save scans" setting is enabled
|
// Ensure that the "save scans" setting is enabled
|
||||||
await setSettingState({
|
await setSettingState({
|
||||||
@ -221,11 +220,14 @@ test('Settings - Admin - Barcode History', async ({ page, request }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings - Admin - Unauthorized', async ({ page }) => {
|
test('Settings - Admin - Unauthorized', async ({ browser }) => {
|
||||||
// Try to access "admin" page with a non-staff user
|
// Try to access "admin" page with a non-staff user
|
||||||
await doQuickLogin(page, 'allaccess', 'nolimits');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'allaccess',
|
||||||
|
password: 'nolimits',
|
||||||
|
url: 'settings/admin/'
|
||||||
|
});
|
||||||
|
|
||||||
await navigate(page, 'settings/admin/');
|
|
||||||
await page.waitForURL('**/settings/admin/**');
|
await page.waitForURL('**/settings/admin/**');
|
||||||
|
|
||||||
// Should get a permission denied message
|
// Should get a permission denied message
|
||||||
@ -252,9 +254,12 @@ test('Settings - Admin - Unauthorized', async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test for user auth configuration
|
// Test for user auth configuration
|
||||||
test('Settings - Auth - Email', async ({ page }) => {
|
test('Settings - Auth - Email', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'allaccess', 'nolimits');
|
const page = await doCachedLogin(browser, {
|
||||||
await navigate(page, 'settings/user/');
|
username: 'allaccess',
|
||||||
|
password: 'nolimits',
|
||||||
|
url: 'settings/user/'
|
||||||
|
});
|
||||||
|
|
||||||
await loadTab(page, 'Security');
|
await loadTab(page, 'Security');
|
||||||
|
|
||||||
@ -269,9 +274,8 @@ test('Settings - Auth - Email', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Remove' }).click();
|
await page.getByRole('button', { name: 'Remove' }).click();
|
||||||
|
|
||||||
await page.getByText('Currently no email addresses are registered').waitFor();
|
await page.getByText('Currently no email addresses are registered').waitFor();
|
||||||
|
|
||||||
await page.waitForTimeout(2500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function testColorPicker(page, ref: string) {
|
async function testColorPicker(page, ref: string) {
|
||||||
const element = page.getByLabel(ref);
|
const element = page.getByLabel(ref);
|
||||||
await element.click();
|
await element.click();
|
||||||
|
@ -4,13 +4,11 @@ import {
|
|||||||
navigate,
|
navigate,
|
||||||
setTableChoiceFilter
|
setTableChoiceFilter
|
||||||
} from './helpers.js';
|
} from './helpers.js';
|
||||||
import { doQuickLogin } from './login.js';
|
import { doCachedLogin } from './login.js';
|
||||||
|
|
||||||
test('Tables - Filters', async ({ page }) => {
|
|
||||||
await doQuickLogin(page);
|
|
||||||
|
|
||||||
|
test('Tables - Filters', async ({ browser }) => {
|
||||||
// Head to the "build order list" page
|
// Head to the "build order list" page
|
||||||
await navigate(page, 'manufacturing/index/');
|
const page = await doCachedLogin(browser, { url: 'manufacturing/index/' });
|
||||||
|
|
||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
|
|
||||||
@ -41,11 +39,11 @@ test('Tables - Filters', async ({ page }) => {
|
|||||||
await clearTableFilters(page);
|
await clearTableFilters(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Tables - Columns', async ({ page }) => {
|
test('Tables - Columns', async ({ browser }) => {
|
||||||
await doQuickLogin(page);
|
|
||||||
|
|
||||||
// Go to the "stock list" page
|
// Go to the "stock list" page
|
||||||
await navigate(page, 'stock/location/index/stock-items');
|
const page = await doCachedLogin(browser, {
|
||||||
|
url: 'stock/location/index/stock-items'
|
||||||
|
});
|
||||||
|
|
||||||
// Open column selector
|
// Open column selector
|
||||||
await page.getByLabel('table-select-columns').click();
|
await page.getByLabel('table-select-columns').click();
|
||||||
@ -64,6 +62,4 @@ test('Tables - Columns', async ({ page }) => {
|
|||||||
await page.getByRole('menuitem', { name: 'Target Date' }).click();
|
await page.getByRole('menuitem', { name: 'Target Date' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Reference', exact: true }).click();
|
await page.getByRole('menuitem', { name: 'Reference', exact: true }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Project Code' }).click();
|
await page.getByRole('menuitem', { name: 'Project Code' }).click();
|
||||||
|
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { test } from '../baseFixtures';
|
import { test } from '../baseFixtures';
|
||||||
import { navigate } from '../helpers';
|
import { navigate } from '../helpers';
|
||||||
import { doQuickLogin } from '../login';
|
import { doCachedLogin } from '../login';
|
||||||
|
|
||||||
test('PUI - Admin - Parameter', async ({ page }) => {
|
test('PUI - Admin - Parameter', async ({ browser }) => {
|
||||||
await doQuickLogin(page, 'admin', 'inventree');
|
const page = await doCachedLogin(browser, {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'inventree'
|
||||||
|
});
|
||||||
await page.getByRole('button', { name: 'admin' }).click();
|
await page.getByRole('button', { name: 'admin' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Admin Center' }).click();
|
await page.getByRole('menuitem', { name: 'Admin Center' }).click();
|
||||||
await page.getByRole('tab', { name: 'Part Parameters' }).click();
|
await page.getByRole('tab', { name: 'Part Parameters' }).click();
|
||||||
|
@ -6,15 +6,23 @@ import license from 'rollup-plugin-license';
|
|||||||
import { defineConfig, splitVendorChunkPlugin } from 'vite';
|
import { defineConfig, splitVendorChunkPlugin } from 'vite';
|
||||||
import istanbul from 'vite-plugin-istanbul';
|
import istanbul from 'vite-plugin-istanbul';
|
||||||
|
|
||||||
|
// Detect if the current environment is WSL
|
||||||
|
// Required for enabling file system polling
|
||||||
const IS_IN_WSL = platform().includes('WSL') || release().includes('WSL');
|
const IS_IN_WSL = platform().includes('WSL') || release().includes('WSL');
|
||||||
const is_coverage = process.env.VITE_COVERAGE === 'true';
|
|
||||||
|
// Detect if code coverage is enabled (runs in GitHub CI)
|
||||||
|
const IS_COVERAGE = !!process.env.VITE_COVERAGE_BUILD;
|
||||||
|
|
||||||
if (IS_IN_WSL) {
|
if (IS_IN_WSL) {
|
||||||
console.log('WSL detected: using polling for file system events');
|
console.log('WSL detected: using polling for file system events');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Output directory for the built files
|
||||||
|
const OUTPUT_DIR = '../../src/backend/InvenTree/web/static/web';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig(({ command, mode }) => {
|
||||||
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
react({
|
react({
|
||||||
babel: {
|
babel: {
|
||||||
@ -48,11 +56,13 @@ export default defineConfig({
|
|||||||
uploadToken: process.env.CODECOV_TOKEN
|
uploadToken: process.env.CODECOV_TOKEN
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
base: '',
|
// When building, set the base path to an empty string
|
||||||
|
// This is required to ensure that the static path prefix is observed
|
||||||
|
base: command == 'build' ? '' : undefined,
|
||||||
build: {
|
build: {
|
||||||
manifest: true,
|
manifest: true,
|
||||||
outDir: '../../src/backend/InvenTree/web/static/web',
|
outDir: OUTPUT_DIR,
|
||||||
sourcemap: is_coverage
|
sourcemap: IS_COVERAGE
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
@ -63,9 +73,10 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
// use polling only for WSL as the file system doesn't trigger notifications for Linux apps
|
// Use polling only for WSL as the file system doesn't trigger notifications for Linux apps
|
||||||
// ref: https://github.com/vitejs/vite/issues/1153#issuecomment-785467271
|
// Ref: https://github.com/vitejs/vite/issues/1153#issuecomment-785467271
|
||||||
usePolling: IS_IN_WSL
|
usePolling: IS_IN_WSL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
@ -1519,6 +1519,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
|
resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
|
||||||
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
|
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
|
||||||
|
|
||||||
|
"@messageformat/date-skeleton@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz#3bad068cbf5873d14592cfc7a73dd4d8615e2739"
|
||||||
|
integrity sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==
|
||||||
|
|
||||||
"@messageformat/parser@^5.0.0":
|
"@messageformat/parser@^5.0.0":
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
|
resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
|
||||||
@ -1951,10 +1956,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.19.2"
|
undici-types "~6.19.2"
|
||||||
|
|
||||||
"@types/node@^22.6.0":
|
"@types/node@^22.13.14":
|
||||||
version "22.10.7"
|
version "22.13.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.14.tgz#70d84ec91013dcd2ba2de35532a5a14c2b4cc912"
|
||||||
integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==
|
integrity sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.20.0"
|
undici-types "~6.20.0"
|
||||||
|
|
||||||
@ -3312,6 +3317,11 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
inherits@2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
|
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
|
||||||
|
|
||||||
inquirer@^7.3.3:
|
inquirer@^7.3.3:
|
||||||
version "7.3.3"
|
version "7.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
|
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
|
||||||
@ -3996,6 +4006,14 @@ path-type@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
|
path@^0.12.7:
|
||||||
|
version "0.12.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
|
||||||
|
integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==
|
||||||
|
dependencies:
|
||||||
|
process "^0.11.1"
|
||||||
|
util "^0.10.3"
|
||||||
|
|
||||||
pathe@^1.1.0, pathe@^1.1.2:
|
pathe@^1.1.0, pathe@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
||||||
@ -4112,6 +4130,11 @@ process-on-spawn@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fromentries "^1.2.0"
|
fromentries "^1.2.0"
|
||||||
|
|
||||||
|
process@^0.11.1:
|
||||||
|
version "0.11.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||||
|
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||||
|
|
||||||
prop-types@15.x, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
|
prop-types@15.x, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
@ -4954,6 +4977,13 @@ util-deprecate@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|
||||||
|
util@^0.10.3:
|
||||||
|
version "0.10.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||||
|
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
|
||||||
|
dependencies:
|
||||||
|
inherits "2.0.3"
|
||||||
|
|
||||||
uuid@^8.3.2:
|
uuid@^8.3.2:
|
||||||
version "8.3.2"
|
version "8.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user