mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +01:00
(test) ProductForm
This commit is contained in:
parent
d547660170
commit
e246480049
@ -16,13 +16,14 @@ interface FormRichTextProps<TFieldValues, TContext extends object> extends FormC
|
||||
blockquote?: boolean,
|
||||
link?: boolean,
|
||||
video?: boolean,
|
||||
image?: boolean
|
||||
image?: boolean,
|
||||
ariaLabel?: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is a rich-text editor to use with react-hook-form.
|
||||
*/
|
||||
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled = false, formState, limit, heading, bulletList, blockquote, video, image, link }: FormRichTextProps<TFieldValues, TContext>) => {
|
||||
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled = false, formState, limit, heading, bulletList, blockquote, video, image, link, ariaLabel }: FormRichTextProps<TFieldValues, TContext>) => {
|
||||
const textEditorRef = React.useRef<FabTextEditorRef>();
|
||||
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
|
||||
|
||||
@ -66,7 +67,7 @@ export const FormRichText = <TFieldValues extends FieldValues, TContext extends
|
||||
link={link}
|
||||
disabled={isDisabled}
|
||||
ref={textEditorRef}
|
||||
ariaLabel={label as string}/>
|
||||
ariaLabel={ariaLabel || label as string}/>
|
||||
} />
|
||||
</AbstractFormItem>
|
||||
);
|
||||
|
@ -269,7 +269,8 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
||||
blockquote
|
||||
link
|
||||
limit={6000}
|
||||
id="description" />
|
||||
id="description"
|
||||
ariaLabel={t('app.admin.store.product_form.product_description')} />
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
@ -265,8 +265,8 @@ export const ProductStockForm = <TContext extends object> ({ currentFormValues,
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{stockMovements?.data?.map(movement => <div className="stock-history" key={movement.id}>
|
||||
<div className="stock-item">
|
||||
{stockMovements?.data?.map(movement => <div className="stock-history" key={movement.id} role="list">
|
||||
<div className="stock-item" role="listitem">
|
||||
<p className='title'>{currentFormValues.name}</p>
|
||||
<p>{FormatLib.date(movement.date)}</p>
|
||||
<div className="group">
|
||||
|
@ -14,7 +14,7 @@ export interface Machine {
|
||||
spec?: string,
|
||||
disabled: boolean,
|
||||
slug: string,
|
||||
machine_image_attributes: FileType,
|
||||
machine_image_attributes?: FileType,
|
||||
machine_files_attributes?: Array<FileType>,
|
||||
trainings?: Array<{
|
||||
id: number,
|
||||
|
@ -87,8 +87,8 @@ module.exports = {
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|jpeg|png|gif|tiff|ico|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/__mocks__/fileMock.js',
|
||||
'\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js'
|
||||
'<rootDir>/test/frontend/__mocks__/fileMock.js',
|
||||
'\\.(css|less)$': '<rootDir>/test/frontend/__mocks__/styleMock.js'
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
|
22
test/frontend/__fixtures__/machines.ts
Normal file
22
test/frontend/__fixtures__/machines.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Machine } from '../../../app/frontend/src/javascript/models/machine';
|
||||
|
||||
const machines: Array<Machine> = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Laser cutter',
|
||||
description: 'EPILOG Legend 36EXT',
|
||||
spec: 'Power: 40W, Working area: 914 x 609mm, Maximum material thickness: 305mm',
|
||||
slug: 'laser-cutter',
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '3D printer',
|
||||
description: 'Ultimaker 2',
|
||||
spec: 'Maximum working area: 210 x 210 x 220mm, Mechanical resolution: 0.02mm',
|
||||
slug: '3d-printer',
|
||||
disabled: false
|
||||
}
|
||||
];
|
||||
|
||||
export default machines;
|
7
test/frontend/__fixtures__/product_categories.ts
Normal file
7
test/frontend/__fixtures__/product_categories.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { ProductCategory } from '../../../app/frontend/src/javascript/models/product-category';
|
||||
|
||||
const categories: Array<ProductCategory> = [
|
||||
{ id: 1, name: 'Consumables', slug: 'consumables', products_count: 1, parent_id: null, position: 1 }
|
||||
];
|
||||
|
||||
export default categories;
|
51
test/frontend/__fixtures__/product_stock_movements.ts
Normal file
51
test/frontend/__fixtures__/product_stock_movements.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ProductStockMovement } from '../../../app/frontend/src/javascript/models/product';
|
||||
|
||||
const movements: Array<ProductStockMovement> = [
|
||||
{
|
||||
id: 1,
|
||||
product_id: 1,
|
||||
quantity: 10,
|
||||
reason: 'inward_stock',
|
||||
stock_type: 'internal',
|
||||
remaining_stock: 10,
|
||||
date: '2022-12-05T15:24:00Z'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
product_id: 1,
|
||||
quantity: 85,
|
||||
reason: 'inward_stock',
|
||||
stock_type: 'external',
|
||||
remaining_stock: 85,
|
||||
date: '2022-12-05T15:24:00Z'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
product_id: 2,
|
||||
quantity: 2,
|
||||
reason: 'inward_stock',
|
||||
stock_type: 'internal',
|
||||
remaining_stock: 2,
|
||||
date: '2022-12-05T15:24:00Z'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
product_id: 2,
|
||||
quantity: 12,
|
||||
reason: 'inward_stock',
|
||||
stock_type: 'external',
|
||||
remaining_stock: 15,
|
||||
date: '2022-12-05T15:24:00Z'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
product_id: 2,
|
||||
quantity: 3,
|
||||
reason: 'sold',
|
||||
stock_type: 'external',
|
||||
remaining_stock: 12,
|
||||
date: '2022-12-05T15:24:00Z'
|
||||
}
|
||||
];
|
||||
|
||||
export default movements;
|
49
test/frontend/__fixtures__/products.ts
Normal file
49
test/frontend/__fixtures__/products.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Product } from '../../../app/frontend/src/javascript/models/product';
|
||||
import movements from './product_stock_movements';
|
||||
|
||||
const products: Array<Product> = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'MDF panel',
|
||||
slug: 'mdf-panel',
|
||||
sku: '3-8612',
|
||||
description: 'Medium Density Fiberboard (MDF) is a composite panel product typically consisting of cellulosic fibers combined with a synthetic resin',
|
||||
is_active: true,
|
||||
amount: 47.12,
|
||||
quantity_min: 1,
|
||||
stock: {
|
||||
internal: 10,
|
||||
external: 85
|
||||
},
|
||||
low_stock_alert: true,
|
||||
low_stock_threshold: 20,
|
||||
machine_ids: [],
|
||||
created_at: '2022-12-05T16:15:00Z',
|
||||
product_category_id: 1,
|
||||
product_files_attributes: [],
|
||||
product_images_attributes: [],
|
||||
product_stock_movements_attributes: movements.filter(m => m.product_id === 1)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Particleboard',
|
||||
slug: 'particleboard',
|
||||
sku: '3-7421',
|
||||
description: 'Particleboard is a composite panel product consisting of cellulosic particles of various sizes that are bonded together with a synthetic resin or binder under heat and pressure',
|
||||
is_active: false,
|
||||
stock: {
|
||||
internal: 2,
|
||||
external: 12
|
||||
},
|
||||
low_stock_alert: true,
|
||||
low_stock_threshold: 5,
|
||||
machine_ids: [],
|
||||
created_at: '2022-12-05T17:04:00Z',
|
||||
product_category_id: 1,
|
||||
product_files_attributes: [],
|
||||
product_images_attributes: [],
|
||||
product_stock_movements_attributes: movements.filter(m => m.product_id === 2)
|
||||
}
|
||||
];
|
||||
|
||||
export default products;
|
29
test/frontend/__lib__/ui-router.ts
Normal file
29
test/frontend/__lib__/ui-router.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { UIRouter } from '@uirouter/angularjs';
|
||||
|
||||
export const uiRouter = {
|
||||
$id: 0,
|
||||
_disposed: false,
|
||||
_disposables: [],
|
||||
_plugins: [],
|
||||
locationService: {},
|
||||
locationConfig: {},
|
||||
trace: {},
|
||||
globals: {
|
||||
current: { name: '' }
|
||||
},
|
||||
viewService: {},
|
||||
transitionService: {
|
||||
onBefore: () => jest.fn()
|
||||
},
|
||||
urlMatcherFactory: {},
|
||||
urlRouter: {},
|
||||
urlRouterProvider: {},
|
||||
urlService: {},
|
||||
stateRegistry: {},
|
||||
stateService: {},
|
||||
stateProvider: {},
|
||||
disposable: jest.fn(),
|
||||
dispose: jest.fn(),
|
||||
plugin: jest.fn(),
|
||||
getPlugin: jest.fn()
|
||||
} as unknown as UIRouter;
|
@ -5,6 +5,10 @@ import plans from '../__fixtures__/plans';
|
||||
import planCategories from '../__fixtures__/plan_categories';
|
||||
import { partners, managers, users } from '../__fixtures__/users';
|
||||
import { settings } from '../__fixtures__/settings';
|
||||
import products from '../__fixtures__/products';
|
||||
import productCategories from '../__fixtures__/product_categories';
|
||||
import productStockMovements from '../__fixtures__/product_stock_movements';
|
||||
import machines from '../__fixtures__/machines';
|
||||
|
||||
export const server = setupServer(
|
||||
rest.get('/api/groups', (req, res, ctx) => {
|
||||
@ -50,6 +54,25 @@ export const server = setupServer(
|
||||
}),
|
||||
rest.patch('/api/settings/bulk_update', (req, res, ctx) => {
|
||||
return res(ctx.json(req.body));
|
||||
}),
|
||||
rest.get('/api/product_categories', (req, res, ctx) => {
|
||||
return res(ctx.json(productCategories));
|
||||
}),
|
||||
rest.get('/api/products', (req, res, ctx) => {
|
||||
return res(ctx.json(products));
|
||||
}),
|
||||
rest.get('/api/products/:id/stock_movements', (req, res, ctx) => {
|
||||
const { id } = req.params;
|
||||
return res(ctx.json({
|
||||
page: 1,
|
||||
total_pages: Math.ceil(productStockMovements.length / 10),
|
||||
page_size: 10,
|
||||
total_count: productStockMovements.length,
|
||||
data: productStockMovements.filter(m => String(m.product_id) === id)
|
||||
}));
|
||||
}),
|
||||
rest.get('/api/machines', (req, res, ctx) => {
|
||||
return res(ctx.json(machines));
|
||||
})
|
||||
);
|
||||
|
||||
|
94
test/frontend/components/store/product-form.test.tsx
Normal file
94
test/frontend/components/store/product-form.test.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { ProductForm } from '../../../../app/frontend/src/javascript/components/store/product-form';
|
||||
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import products from '../../__fixtures__/products';
|
||||
import machines from '../../__fixtures__/machines';
|
||||
import { uiRouter } from '../../__lib__/ui-router';
|
||||
|
||||
describe('ProductForm', () => {
|
||||
const onError = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
|
||||
test('render ProductForm', async () => {
|
||||
render(<ProductForm product={products[0]}
|
||||
title={'Update a product'}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
uiRouter={uiRouter} />);
|
||||
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.store.product_form.linking_product_to_category/ }));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.name/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.sku/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.slug/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_active_price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.quantity_min/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /app.admin.store.product_form.add_product_image/ })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.linking_product_to_category/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { name: /app.admin.store.product_form.assigning_machines/ })).toBeInTheDocument();
|
||||
for (const machine of machines) {
|
||||
expect(screen.getByLabelText(machine.name)).toBeInTheDocument();
|
||||
}
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.product_description/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /app.admin.store.product_form.add_product_file/ })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.advanced_accounting_form.code/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.advanced_accounting_form.analytical_section/)).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('tab', { name: /app.admin.store.product_form.stock_management/ }));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByRole('heading', { name: /app.admin.store.product_stock_form.stock_up_to_date/ })).toBeInTheDocument()
|
||||
);
|
||||
expect(screen.getByLabelText(/app.admin.store.product_stock_form.stock_threshold_toggle/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('toggle off the price', async () => {
|
||||
render(<ProductForm product={products[0]}
|
||||
title={'Update a product'}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
uiRouter={uiRouter} />);
|
||||
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.store.product_form.linking_product_to_category/ }));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.quantity_min/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).toBeChecked();
|
||||
fireEvent.click(screen.getByLabelText(/app.admin.store.product_form.is_active_price/));
|
||||
expect(screen.queryByLabelText(/app.admin.store.product_form.price/)).toBeNull();
|
||||
expect(screen.queryByLabelText(/app.admin.store.product_form.quantity_min/)).toBeNull();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).not.toBeChecked();
|
||||
});
|
||||
|
||||
test('toggle off the visibility', async () => {
|
||||
render(<ProductForm product={products[0]}
|
||||
title={'Update a product'}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
uiRouter={uiRouter} />);
|
||||
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.store.product_form.linking_product_to_category/ }));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_active_price/)).toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.quantity_min/)).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).not.toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_active_price/)).toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.quantity_min/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('toggle on the visibility', async () => {
|
||||
render(<ProductForm product={products[1]}
|
||||
title={'Update a product'}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
uiRouter={uiRouter} />);
|
||||
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.store.product_form.linking_product_to_category/ }));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).not.toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_active_price/)).not.toBeChecked();
|
||||
expect(screen.queryByLabelText(/app.admin.store.product_form.price/)).toBeNull();
|
||||
expect(screen.queryByLabelText(/app.admin.store.product_form.quantity_min/)).toBeNull();
|
||||
fireEvent.click(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/));
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_show_in_store/)).toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.is_active_price/)).toBeChecked();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.price/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/app.admin.store.product_form.quantity_min/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user