Compare commits

...

16 Commits

Author SHA1 Message Date
RustamRu
351420bc62 0.9.1
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-23 12:37:44 +03:00
RustamRu
61b042eee6 fix: image input accept, i18n (#88)
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-23 12:37:04 +03:00
RustamRu
ac006267a2 0.9.0
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-23 12:11:20 +03:00
RustamRu
63d9d069c0 feat: add car image feature
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-23 12:08:50 +03:00
c83ebf02bc Merge pull request 'feature/upload-car-image' (#90) from feature/upload-car-image into main
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
Reviewed-on: #90
2025-02-23 12:02:39 +03:00
RustamRu
1968df7bb3 fix: getOrder test (#88)
All checks were successful
it-academy/dry-wash-pl/pipeline/pr-main This commit looks good
2025-02-23 11:58:56 +03:00
RustamRu
811e0e3f24 Merge remote-tracking branch 'origin/main' into feature/upload-car-image
Some checks failed
it-academy/dry-wash-pl/pipeline/pr-main There was a failure building this commit
2025-02-23 11:47:04 +03:00
a2ffd6f38f Merge pull request 'fix tests error' (#89) from debug into main
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
Reviewed-on: #89
2025-02-23 11:32:09 +03:00
Primakov Alexandr Alexandrovich
7b3889aa02 run last one
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
it-academy/dry-wash-pl/pipeline/pr-main This commit looks good
2025-02-20 14:15:25 +03:00
Primakov Alexandr Alexandrovich
cee124fca5 other 2
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-20 14:10:46 +03:00
Primakov Alexandr Alexandrovich
b77eccc8e8 test: улучшены описания тестов страницы просмотра заказа
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
- Добавлены более информативные описания тестовых сценариев
- Улучшена читаемость тестов за счет детальных названий на русском языке
2025-02-20 14:04:32 +03:00
Primakov Alexandr Alexandrovich
ebfaa7ea8f orders page
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-20 14:00:34 +03:00
Primakov Alexandr Alexandrovich
0027cc09b1 masters test
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-20 13:55:05 +03:00
Primakov Alexandr Alexandrovich
dd612d662c debug
All checks were successful
it-academy/dry-wash-pl/pipeline/head This commit looks good
2025-02-20 13:44:38 +03:00
Primakov Alexandr Alexandrovich
69251745fa try fix
Some checks failed
it-academy/dry-wash-pl/pipeline/head There was a failure building this commit
2025-02-20 13:32:39 +03:00
Primakov Alexandr Alexandrovich
253e3b3856 try skip one test
Some checks failed
it-academy/dry-wash-pl/pipeline/head There was a failure building this commit
2025-02-20 13:26:35 +03:00
21 changed files with 145 additions and 62 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"i18n-ally.localesPaths": [
"locales"
]
}

32
Jenkinsfile vendored
View File

@ -1,7 +1,7 @@
pipeline { pipeline {
agent { agent {
docker { docker {
image 'node:20' image 'node:22'
} }
} }
@ -30,25 +30,21 @@ pipeline {
} }
} }
stage('checks') { stage('eslint') {
parallel { steps {
stage('eslint') { sh 'npm run eslint'
steps { }
sh 'npm run eslint' }
}
}
stage('test') { stage('test') {
steps { steps {
sh 'npm run test' sh 'npm run test'
} }
} }
stage('build') { stage('build') {
steps { steps {
sh 'npm run build' sh 'npm run build'
}
}
} }
} }

View File

@ -21,7 +21,16 @@ module.exports = {
features: { features: {
'dry-wash': { 'dry-wash': {
// add your features here in the format [featureName]: { value: string } // add your features here in the format [featureName]: { value: string }
'order-view-status-polling': { value: '3000' } "order-view-status-polling": {
"on": true,
"value": "3000",
"key": "order-view-status-polling"
},
"car-img-upload": {
"on": true,
"value": "true",
"key": "car-img-upload"
}
}, },
}, },
config: { config: {

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "dry-wash", "name": "dry-wash",
"version": "0.8.0", "version": "0.9.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "dry-wash", "name": "dry-wash",
"version": "0.8.0", "version": "0.9.1",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@babel/core": "^7.26.7", "@babel/core": "^7.26.7",

View File

@ -1,6 +1,6 @@
{ {
"name": "dry-wash", "name": "dry-wash",
"version": "0.8.0", "version": "0.9.1",
"description": "<a id=\"readme-top\"></a>", "description": "<a id=\"readme-top\"></a>",
"main": "./src/index.tsx", "main": "./src/index.tsx",
"scripts": { "scripts": {
@ -9,8 +9,8 @@
"build": "npm run clean && brojs build --dev", "build": "npm run clean && brojs build --dev",
"build:prod": "npm run clean && brojs build", "build:prod": "npm run clean && brojs build",
"clean": "rimraf dist", "clean": "rimraf dist",
"eslint": "npx eslint .", "eslint": "npx eslint src",
"eslint:fix": "npx eslint . --fix", "eslint:fix": "npx eslint src --fix",
"preversion": "npm run eslint" "preversion": "npm run eslint"
}, },
"keywords": [], "keywords": [],

View File

@ -11,5 +11,8 @@ export const FEATURE = {
return interval; return interval;
} }
} }
},
carImageUpload: {
isOn: Boolean(features?.['car-img-upload'])
} }
}; };

View File

@ -13,6 +13,7 @@ export const extractErrorMessageFromResponse = ({
}: FetchBaseQueryError) => { }: FetchBaseQueryError) => {
if ( if (
typeof data === 'object' && typeof data === 'object' &&
data !== null &&
'error' in data && 'error' in data &&
typeof data.error === 'string' typeof data.error === 'string'
) { ) {

View File

@ -7,7 +7,9 @@ export const store = configureStore({
[api.reducerPath]: api.reducer, [api.reducerPath]: api.reducer,
}, },
middleware: (getDefaultMiddleware) => middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware), getDefaultMiddleware({
serializableCheck: false
}).concat(api.middleware),
}); });
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>;

View File

@ -28,7 +28,6 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
componentDidCatch(error: Error, errorInfo: ErrorInfo): void { componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
console.error('Error caught by ErrorBoundary:', error, errorInfo); console.error('Error caught by ErrorBoundary:', error, errorInfo);
console.error('4545');
this.setState({ error, errorInfo }); this.setState({ error, errorInfo });
} }

View File

@ -24,6 +24,10 @@ jest.mock('@brojs/cli', () => {
describe('ErrorBoundary', () => { describe('ErrorBoundary', () => {
it('должен отобразить запасной UI при ошибке', async () => { it('должен отобразить запасной UI при ошибке', async () => {
// Подавляем вывод ошибки в консоль во время теста
const consoleSpy = jest.spyOn(console, 'error');
consoleSpy.mockImplementation(() => {});
const { container } = render( const { container } = render(
<Provider store={store}> <Provider store={store}>
<ErrorBoundary> <ErrorBoundary>
@ -39,7 +43,9 @@ describe('ErrorBoundary', () => {
); );
expect(button).not.toBeNull(); expect(button).not.toBeNull();
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
// Восстанавливаем console.error после теста
consoleSpy.mockRestore();
}); });
}); });

View File

@ -64,7 +64,7 @@ export const CarImageForm: FC<CarImageFormProps> = memo(function CarImageForm({
<Input <Input
{...field} {...field}
ref={fileInputRef} ref={fileInputRef}
accept='.jpg,.png' accept='image/png,image/jpeg'
value={value?.fileName} value={value?.fileName}
onChange={(event) => { onChange={(event) => {
onChange(event.target.files[0]); onChange(event.target.files[0]);

View File

@ -11,7 +11,7 @@ export const useHandleUploadCarImageResponse = (query: {
}) => { }) => {
const toast = useToast(); const toast = useToast();
const { t } = useTranslation('~', { const { t } = useTranslation('~', {
keyPrefix: 'dry-wash.order-create.upload-car-image-query', keyPrefix: 'dry-wash.order-view.upload-car-image-query',
}); });
useEffect(() => { useEffect(() => {

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Master Page render 1`] = ` exports[`Master Page should display master list and show details when master button is clicked 1`] = `
<div> <div>
<div <div
class="css-1yeiifd" class="css-1yeiifd"

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Order View page, initial load shows order details 1`] = ` exports[`Страница просмотра заказа отображает детали заказа после успешной загрузки 1`] = `
<div> <div>
<div <div
class="chakra-container css-3n6qh3" class="chakra-container css-3n6qh3"
@ -16,7 +16,6 @@ exports[`Order View page, initial load shows order details 1`] = `
</h2> </h2>
<div <div
class="chakra-stack css-1n38vgh" class="chakra-stack css-1n38vgh"
created="2025-01-19T14:04:02.985Z"
data-testid="order-details" data-testid="order-details"
> >
<div <div
@ -26,6 +25,9 @@ exports[`Order View page, initial load shows order details 1`] = `
class="chakra-heading css-1jb3vzl" class="chakra-heading css-1jb3vzl"
> >
Заказ №{{number}} Заказ №{{number}}
(
Sunday, January 19, 2025 5:04 PM
)
</h2> </h2>
<span <span
class="css-6jfsiv" class="css-6jfsiv"
@ -126,7 +128,7 @@ exports[`Order View page, initial load shows order details 1`] = `
</div> </div>
`; `;
exports[`Order View page, initial load shows order details loading 1`] = ` exports[`Страница просмотра заказа отображает индикатор загрузки деталей заказа 1`] = `
<div> <div>
<div <div
class="chakra-container css-3n6qh3" class="chakra-container css-3n6qh3"
@ -171,7 +173,7 @@ exports[`Order View page, initial load shows order details loading 1`] = `
</div> </div>
`; `;
exports[`Order View page, initial load shows order error 1`] = ` exports[`Страница просмотра заказа отображает ошибку при некорректном ID заказа 1`] = `
<div> <div>
<div <div
class="chakra-container css-3n6qh3" class="chakra-container css-3n6qh3"

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`order page получение списка заказов 1`] = ` exports[`Страница заказов должна корректно отображать список заказов после загрузки данных 1`] = `
<div> <div>
<div <div
class="css-1yeiifd" class="css-1yeiifd"
@ -75,7 +75,7 @@ exports[`order page получение списка заказов 1`] = `
<p <p
class="chakra-text css-52ukzg" class="chakra-text css-52ukzg"
> >
16.02.2025 23.02.2025
</p> </p>
<button <button
class="chakra-button css-ez23ye" class="chakra-button css-ez23ye"

View File

@ -54,6 +54,41 @@ const server = setupServer(
], ],
}); });
}), }),
http.post('/api/arm/orders', () => {
return HttpResponse.json({
success: true,
body: [
{
id: 'order1',
carNumber: 'A123BC',
startWashTime: '2024-11-24T10:30:00.000Z',
endWashTime: '2024-11-24T16:30:00.000Z',
orderDate: '2024-11-24T08:41:46.366Z',
status: 'pending',
phone: '79001234563',
location: 'Казань, ул. Баумана, 1',
master: {
name: 'Олег Макаров',
phone: '79001234567',
id: '23423442',
},
notes: '',
},
{
id: 'order2',
carNumber: 'A245BC',
startWashTime: '2024-11-24T11:30:00.000Z',
endWashTime: '2024-11-24T17:30:00.000Z',
orderDate: '2024-11-24T07:40:46.366Z',
status: 'progress',
phone: '79001234567',
location: 'Казань, ул. Баумана, 43',
master: [],
notes: '',
},
],
});
}),
); );
jest.mock('@brojs/cli', () => { jest.mock('@brojs/cli', () => {
@ -70,11 +105,7 @@ describe('Master Page', () => {
afterEach(() => server.resetHandlers()); afterEach(() => server.resetHandlers());
afterAll(() => server.close()); afterAll(() => server.close());
it('render ', async () => { it('should display master list and show details when master button is clicked', async () => {
server.events.on('request:start', ({ request }) => {
console.log('Outgoing:', request.method, request.url);
});
const { container } = render( const { container } = render(
<Provider store={store}> <Provider store={store}>
<ChakraProvider theme={chakraTheme}> <ChakraProvider theme={chakraTheme}>
@ -88,10 +119,14 @@ describe('Master Page', () => {
); );
const button = await waitFor(() => screen.getByTestId('master-button')); const button = await waitFor(() => screen.getByTestId('master-button'));
fireEvent.click(button); fireEvent.click(button);
await waitFor(() => screen.getByText('Иван Иванов')); // Проверяем отображение всех мастеров
await waitFor(() => {
expect(screen.getByText('Иван Иванов')).toBeInTheDocument();
expect(screen.getByText('Олег Макаров')).toBeInTheDocument();
expect(screen.getByText('Иван Галкин')).toBeInTheDocument();
});
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });

View File

@ -11,12 +11,12 @@ jest.mock('react-router-dom', () => ({
useParams: jest.fn(), useParams: jest.fn(),
})); }));
describe('Order View page, initial load', () => { describe('Страница просмотра заказа', () => {
beforeAll(() => server.listen()); beforeAll(() => server.listen());
afterEach(() => server.resetHandlers()); afterEach(() => server.resetHandlers());
afterAll(() => server.close()); afterAll(() => server.close());
test('shows order details loading', () => { test('отображает индикатор загрузки деталей заказа', () => {
(useParams as jest.Mock).mockReturnValue({ orderId: 'id1' }); (useParams as jest.Mock).mockReturnValue({ orderId: 'id1' });
const { container } = render( const { container } = render(
@ -33,7 +33,7 @@ describe('Order View page, initial load', () => {
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('shows order details', async () => { test('отображает детали заказа после успешной загрузки', async () => {
(useParams as jest.Mock).mockReturnValue({ orderId: 'id1' }); (useParams as jest.Mock).mockReturnValue({ orderId: 'id1' });
const { container } = render( const { container } = render(
@ -52,7 +52,7 @@ describe('Order View page, initial load', () => {
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('shows order error', async () => { test('отображает ошибку при некорректном ID заказа', async () => {
(useParams as jest.Mock).mockReturnValue({ orderId: null }); (useParams as jest.Mock).mockReturnValue({ orderId: null });
const { container } = render( const { container } = render(

View File

@ -46,10 +46,6 @@ describe('order page', () => {
afterAll(() => server.close()); afterAll(() => server.close());
it('получение пустого списка', async () => { it('получение пустого списка', async () => {
server.events.on('request:start', ({ request }) => {
console.log('Outgoing:', request.method, request.url);
});
render( render(
<Provider store={store}> <Provider store={store}>
<ChakraProvider theme={chakraTheme}> <ChakraProvider theme={chakraTheme}>

View File

@ -21,9 +21,43 @@ import Page from '../arm';
import { PageSpinner } from '../../components'; import { PageSpinner } from '../../components';
const server = setupServer( const server = setupServer(
http.get('/api/arm/orders', () => { http.post('/api/arm/orders', () => {
return HttpResponse.json({}, { status: 500 }); return HttpResponse.json({}, { status: 500 });
}), }),
http.get('/api/arm/masters', () => {
return HttpResponse.json({
success: true,
body: [
{
id: '4545423234',
name: 'Иван Иванов',
phone: '+7 900 123 45 67',
},
{
name: 'Олег Макаров',
phone: '79001234567',
id: '23423442',
},
{
id: '345354234',
name: 'Иван Галкин',
schedule: [
{
id: 'order1',
startWashTime: '2024-11-24T10:30:00.000Z',
endWashTime: '2024-11-24T16:30:00.000Z',
},
{
id: 'order2',
startWashTime: '2024-11-24T11:30:00.000Z',
endWashTime: '2024-11-24T17:30:00.000Z',
},
],
phone: '+7 900 123 45 67',
},
],
});
}),
); );
jest.mock('@brojs/cli', () => { jest.mock('@brojs/cli', () => {

View File

@ -98,16 +98,12 @@ jest.mock('@brojs/cli', () => {
}; };
}); });
describe('order page', () => { describe('Страница заказов', () => {
beforeAll(() => server.listen()); beforeAll(() => server.listen());
afterEach(() => server.resetHandlers()); afterEach(() => server.resetHandlers());
afterAll(() => server.close()); afterAll(() => server.close());
it('получение списка заказов ', async () => { it('должна корректно отображать список заказов после загрузки данных', async () => {
server.events.on('request:start', ({ request }) => {
console.log('Outgoing:', request.method, request.url);
});
const { container } = render( const { container } = render(
<Provider store={store}> <Provider store={store}>
<ChakraProvider theme={chakraTheme}> <ChakraProvider theme={chakraTheme}>

View File

@ -70,7 +70,7 @@ const Page: FC = () => {
<> <>
<> <>
{isSuccess && ( {isSuccess && (
<VStack p={4} alignItems='flex-start' gap={4}> <VStack p={4} alignItems='flex-start' gap={4} data-testid='order-details'>
<OrderDetails <OrderDetails
orderNumber={order.orderNumber} orderNumber={order.orderNumber}
status={order.status} status={order.status}
@ -82,9 +82,8 @@ const Page: FC = () => {
startWashTime={order.startWashTime} startWashTime={order.startWashTime}
endWashTime={order.endWashTime} endWashTime={order.endWashTime}
created={order.created} created={order.created}
data-testid='order-details'
/> />
<CarImageForm orderId={orderId} /> {FEATURE.carImageUpload.isOn && <CarImageForm orderId={orderId} />}
</VStack> </VStack>
)} )}
</> </>