fix: update vite because of vulnerability (#3283)
### **PR Type** Bug fix, Enhancement ___ ### **Description** - Update Vite to address security vulnerability - Upgrade dependencies in Vue examples - Add 'type: module' to Vue quickstart package - Update resolutions for Vite versions ___ ### **Changes walkthrough** 📝 <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td><table> <tr> <td> <details> <summary><strong>stale-horses-run.md</strong><dd><code>Add changeset for Vite vulnerability fix</code> </dd></summary> <hr> .changeset/stale-horses-run.md <li>Add new changeset file<br> <li> List affected packages for patch update<br> <li> Describe fix for Vite vulnerability </details> </td> <td><a href="https://github.com/nhost/nhost/pull/3283/files#diff-79e66d2654e3803067439855123d20d162193a019ecf68b5b45ee1d0e344949d">+13/-0</a> </td> </tr> </table></td></tr><tr><td><strong>Dependencies</strong></td><td><table> <tr> <td> <details> <summary><strong>package.json</strong><dd><code>Update sass dependency in Vue Apollo example</code> </dd></summary> <hr> examples/vue-apollo/package.json - Update sass dependency from 1.32.0 to 1.86.1 </details> </td> <td><a href="https://github.com/nhost/nhost/pull/3283/files#diff-fc4298d3512fdd9a3d871f9f182fe871c8beccd1580f864a271ddfb32005feef">+1/-1</a> </td> </tr> </table></td></tr><tr><td><strong>Enhancement</strong></td><td><table> <tr> <td> <details> <summary><strong>package.json</strong><dd><code>Update package configuration and dependencies in Vue quickstart</code></dd></summary> <hr> examples/vue-quickstart/package.json <li>Add "type": "module" to package.json<br> <li> Update @unocss/reset from 0.33.5 to 66.1.0-beta.8<br> <li> Update unocss from 0.33.5 to 66.1.0-beta.8 </details> </td> <td><a href="https://github.com/nhost/nhost/pull/3283/files#diff-85166d1137e29a5275f991e1e94a0c9d5b83ac7504463ba76f9187b2b750c895">+3/-2</a> </td> </tr> </table></td></tr><tr><td><strong>Bug fix</strong></td><td><table> <tr> <td> <details> <summary><strong>package.json</strong><dd><code>Add Vite version resolutions to address vulnerabilities</code> </dd></summary> <hr> package.json - Add resolutions for Vite versions 5.4.16 and 6.2.4 </details> </td> <td><a href="https://github.com/nhost/nhost/pull/3283/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+3/-1</a> </td> </tr> </table></td></tr></tr></tbody></table> ___ > <details> <summary> Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about PR-Agent usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>
This commit is contained in:
13
.changeset/stale-horses-run.md
Normal file
13
.changeset/stale-horses-run.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@nhost-examples/vue-quickstart': patch
|
||||
'@nhost-examples/vue-apollo': patch
|
||||
'@nhost/dashboard': patch
|
||||
'@nhost-examples/codegen-react-apollo': patch
|
||||
'@nhost-examples/codegen-react-query': patch
|
||||
'@nhost-examples/codegen-react-urql': patch
|
||||
'@nhost-examples/sveltekit': patch
|
||||
'@nhost-examples/react-apollo': patch
|
||||
'@nhost-examples/react-gqty': patch
|
||||
---
|
||||
|
||||
fix: update vite because of vulnerability
|
||||
@@ -4,7 +4,13 @@ import {
|
||||
mockMatchMediaValue,
|
||||
} from '@/tests/mocks';
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import { mockPointerEvent, render, screen, waitFor } from '@/tests/testUtils';
|
||||
import {
|
||||
clickOnElement,
|
||||
mockPointerEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '@/tests/testUtils';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
@@ -89,8 +95,6 @@ describe('ImportBackupContent', () => {
|
||||
server.use(getOrganization);
|
||||
server.use(getProjectsQuery);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<TestComponent />);
|
||||
expect(
|
||||
await screen.getByText(
|
||||
@@ -100,7 +104,7 @@ describe('ImportBackupContent', () => {
|
||||
|
||||
const projectComboBox = await screen.findByRole('combobox');
|
||||
|
||||
await user.click(projectComboBox);
|
||||
await clickOnElement(projectComboBox);
|
||||
// check for only projects from the same region are listed
|
||||
expect(screen.getByRole('option', { name: /pitr14/i })).toBeInTheDocument();
|
||||
expect(
|
||||
@@ -147,13 +151,15 @@ describe('ImportBackupContent', () => {
|
||||
|
||||
const projectComboBox = await screen.findByRole('combobox');
|
||||
|
||||
await user.click(projectComboBox);
|
||||
await clickOnElement(projectComboBox);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('option', {
|
||||
name: 'pitr14 (us-east-1)',
|
||||
}),
|
||||
);
|
||||
await waitFor(async () => {
|
||||
await user.click(
|
||||
screen.getByRole('option', {
|
||||
name: 'pitr14 (us-east-1)',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
await screen.getByText('Import backup from pitr14 (us-east-1)'),
|
||||
@@ -162,8 +168,7 @@ describe('ImportBackupContent', () => {
|
||||
const startImportButton = await screen.getByRole('button', {
|
||||
name: 'Start import',
|
||||
});
|
||||
|
||||
await user.click(startImportButton);
|
||||
await clickOnElement(startImportButton);
|
||||
|
||||
await waitFor(async () =>
|
||||
expect(
|
||||
@@ -175,7 +180,7 @@ describe('ImportBackupContent', () => {
|
||||
name: /UTC/i,
|
||||
});
|
||||
|
||||
await user.click(dateTimePickerButton);
|
||||
await clickOnElement(dateTimePickerButton);
|
||||
|
||||
await waitFor(async () =>
|
||||
expect(
|
||||
@@ -183,18 +188,19 @@ describe('ImportBackupContent', () => {
|
||||
).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
await user.click(await screen.getByText('13'));
|
||||
await clickOnElement(await screen.getByText('13'));
|
||||
|
||||
const hoursInput = await screen.getByLabelText('Hours');
|
||||
await user.type(hoursInput, '18');
|
||||
|
||||
await waitFor(async () => {
|
||||
await user.type(hoursInput, '18');
|
||||
});
|
||||
const updatedDateTimeButton = await screen.getByRole('button', {
|
||||
name: /UTC/i,
|
||||
});
|
||||
expect(updatedDateTimeButton).toHaveTextContent(
|
||||
'13 Mar 2025, 18:00:05 (UTC+02:00)',
|
||||
);
|
||||
await user.click(await screen.getByRole('button', { name: 'Select' }));
|
||||
await clickOnElement(await screen.getByRole('button', { name: 'Select' }));
|
||||
|
||||
await waitFor(async () =>
|
||||
expect(
|
||||
@@ -208,11 +214,11 @@ describe('ImportBackupContent', () => {
|
||||
|
||||
// check checkboxes
|
||||
|
||||
await user.click(
|
||||
await clickOnElement(
|
||||
await screen.getByLabelText(/I understand that restoring this backup/),
|
||||
);
|
||||
|
||||
await user.click(
|
||||
await clickOnElement(
|
||||
await screen.getByLabelText(/I understand this cannot be undone/),
|
||||
);
|
||||
|
||||
@@ -222,7 +228,7 @@ describe('ImportBackupContent', () => {
|
||||
).not.toBeDisabled(),
|
||||
);
|
||||
|
||||
await user.click(
|
||||
await clickOnElement(
|
||||
await screen.getByRole('button', { name: 'Import backup' }),
|
||||
);
|
||||
|
||||
@@ -234,21 +240,19 @@ describe('ImportBackupContent', () => {
|
||||
mocks.restoreApplicationDatabase.mock.calls[0][0].recoveryTarget,
|
||||
).toBe('2025-03-13T16:00:05.000Z');
|
||||
});
|
||||
// TODO
|
||||
|
||||
test('Pitr is not enabled on project', async () => {
|
||||
server.use(getOrganization);
|
||||
server.use(getProjectsQuery);
|
||||
server.use(getPiTRNotEnabledPostgresSettings);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<TestComponent />);
|
||||
|
||||
const projectComboBox = await screen.findByRole('combobox');
|
||||
|
||||
await user.click(projectComboBox);
|
||||
await clickOnElement(projectComboBox);
|
||||
|
||||
await user.click(
|
||||
await clickOnElement(
|
||||
screen.getByRole('option', {
|
||||
name: 'pitr-not-enabled-usa (us-east-1)',
|
||||
}),
|
||||
|
||||
@@ -8,9 +8,12 @@ import {
|
||||
import updateConfigMutation from '@/tests/msw/mocks/graphql/updateConfigMutation';
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import {
|
||||
clickOnElement,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
userClearElement,
|
||||
userType,
|
||||
waitFor,
|
||||
waitForElementToBeRemoved,
|
||||
within,
|
||||
@@ -19,7 +22,6 @@ import {
|
||||
RESOURCE_MEMORY_MULTIPLIER,
|
||||
RESOURCE_VCPU_MULTIPLIER,
|
||||
} from '@/utils/constants/common';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import ResourcesForm from './ResourcesForm';
|
||||
@@ -66,13 +68,12 @@ test('should show an empty state message that the feature must be enabled if no
|
||||
|
||||
test('should show the sliders if the switch is enabled', async () => {
|
||||
server.use(resourcesUnavailableQuery);
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(await screen.findByText(/enable this feature/i)).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole('checkbox'));
|
||||
await clickOnElement(screen.getByRole('checkbox'));
|
||||
|
||||
expect(screen.queryByText(/enable this feature/i)).not.toBeInTheDocument();
|
||||
expect(screen.getAllByRole('slider')).toHaveLength(9);
|
||||
@@ -133,7 +134,6 @@ test('should update the price when the top slider is changed', async () => {
|
||||
});
|
||||
|
||||
test('should show a validation error when the form is submitted when not everything is allocated', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
@@ -149,7 +149,7 @@ test('should show a validation error when the form is submitted when not everyth
|
||||
9 * RESOURCE_VCPU_MULTIPLIER,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(
|
||||
screen.getByText(/you have 1 vcpus and 2048 mib of memory unused./i),
|
||||
@@ -160,8 +160,6 @@ test('should show a validation error when the form is submitted when not everyth
|
||||
|
||||
test('should show a confirmation dialog when the form is submitted', async () => {
|
||||
server.use(updateConfigMutation);
|
||||
|
||||
const user = userEvent.setup();
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
@@ -209,7 +207,7 @@ test('should show a confirmation dialog when the form is submitted', async () =>
|
||||
5 * RESOURCE_MEMORY_MULTIPLIER,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
expect(
|
||||
@@ -239,7 +237,7 @@ test('should show a confirmation dialog when the form is submitted', async () =>
|
||||
// and we need to return the updated values
|
||||
server.use(resourcesUpdatedQuery);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /confirm/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /confirm/i }));
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('dialog'));
|
||||
|
||||
@@ -254,19 +252,19 @@ test('should show a confirmation dialog when the form is submitted', async () =>
|
||||
});
|
||||
|
||||
test('should display a red button when custom resources are disabled', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('slider', { name: /total available vcpu/i }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getAllByRole('checkbox')[0]);
|
||||
await clickOnElement(screen.getAllByRole('checkbox')[0]);
|
||||
|
||||
await waitFor(() => {});
|
||||
|
||||
expect(screen.getByText(/enable this feature/i)).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
|
||||
@@ -274,30 +272,28 @@ test('should display a red button when custom resources are disabled', async ()
|
||||
screen.getByRole('heading', { name: /disable dedicated resources/i }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /confirm/i })).toHaveStyle({
|
||||
'background-color': '#f13154',
|
||||
'background-color': '#D32F2F',
|
||||
});
|
||||
});
|
||||
|
||||
test('should hide the pricing information when custom resource allocation is disabled', async () => {
|
||||
server.use(updateConfigMutation);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('slider', { name: /total available vcpu/i }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getAllByRole('checkbox')[0]);
|
||||
await clickOnElement(screen.getAllByRole('checkbox')[0]);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
|
||||
server.use(resourcesUnavailableQuery);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /confirm/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /confirm/i }));
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('dialog'));
|
||||
|
||||
@@ -326,8 +322,6 @@ test('should show a warning message when resources are overallocated', async ()
|
||||
});
|
||||
|
||||
test('should change pricing based on selected replicas', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
@@ -340,9 +334,9 @@ test('should change pricing based on selected replicas', async () => {
|
||||
|
||||
const hasuraReplicasInput = screen.getAllByPlaceholderText('Replicas')[0];
|
||||
|
||||
await user.click(hasuraReplicasInput);
|
||||
await user.clear(hasuraReplicasInput);
|
||||
await user.type(hasuraReplicasInput, '2');
|
||||
await clickOnElement(hasuraReplicasInput);
|
||||
await userClearElement(hasuraReplicasInput);
|
||||
await userType(hasuraReplicasInput, '2');
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
@@ -354,9 +348,9 @@ test('should change pricing based on selected replicas', async () => {
|
||||
),
|
||||
);
|
||||
|
||||
await user.click(hasuraReplicasInput);
|
||||
await user.clear(hasuraReplicasInput);
|
||||
await user.type(hasuraReplicasInput, '1');
|
||||
await clickOnElement(hasuraReplicasInput);
|
||||
await userClearElement(hasuraReplicasInput);
|
||||
await userType(hasuraReplicasInput, '1');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/approximate cost:/i)).toHaveTextContent(
|
||||
@@ -365,58 +359,7 @@ test('should change pricing based on selected replicas', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should validate if vCPU and Memory match the 1:2 ratio if more than 1 replica is selected', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('slider', { name: /total available vcpu/i }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', {
|
||||
name: /total available vcpu/i,
|
||||
}),
|
||||
20 * RESOURCE_VCPU_MULTIPLIER,
|
||||
);
|
||||
|
||||
const storageReplicasInput = screen.getAllByPlaceholderText('Replicas')[2];
|
||||
await user.click(storageReplicasInput);
|
||||
await user.clear(storageReplicasInput);
|
||||
await user.type(storageReplicasInput, '2');
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /storage vcpu/i }),
|
||||
1 * RESOURCE_VCPU_MULTIPLIER,
|
||||
);
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /storage memory/i }),
|
||||
6 * RESOURCE_MEMORY_MULTIPLIER,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(screen.getByText(/invalid configuration/i)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/please check the form for errors and the allocation for each service and try again\./i,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
const validationErrorMessage = screen.getByText(
|
||||
/vCPU and Memory for this service must follow a 1:2 ratio when more than one replica is selected or when the autoscaler is activated\./i,
|
||||
);
|
||||
expect(validationErrorMessage).toBeInTheDocument();
|
||||
expect(validationErrorMessage).toHaveStyle({ color: '#f13154' });
|
||||
});
|
||||
});
|
||||
|
||||
test('should take replicas into account when confirming the resources', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
@@ -441,9 +384,9 @@ test('should take replicas into account when confirming the resources', async ()
|
||||
);
|
||||
|
||||
const hasuraReplicasInput = screen.getAllByPlaceholderText('Replicas')[0];
|
||||
await user.click(hasuraReplicasInput);
|
||||
await user.clear(hasuraReplicasInput);
|
||||
await user.type(hasuraReplicasInput, '3');
|
||||
await clickOnElement(hasuraReplicasInput);
|
||||
await userClearElement(hasuraReplicasInput);
|
||||
await userType(hasuraReplicasInput, '3');
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /hasura graphql vcpu/i }),
|
||||
@@ -456,9 +399,9 @@ test('should take replicas into account when confirming the resources', async ()
|
||||
|
||||
const authReplicasInput = screen.getAllByPlaceholderText('Replicas')[1];
|
||||
// setting up auth
|
||||
await user.click(authReplicasInput);
|
||||
await user.clear(authReplicasInput);
|
||||
await user.type(authReplicasInput, '2');
|
||||
await clickOnElement(authReplicasInput);
|
||||
await userClearElement(authReplicasInput);
|
||||
await userType(authReplicasInput, '2');
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /auth vcpu/i }),
|
||||
@@ -471,9 +414,9 @@ test('should take replicas into account when confirming the resources', async ()
|
||||
|
||||
const storageReplicasInput = screen.getAllByPlaceholderText('Replicas')[2];
|
||||
// setting up storage
|
||||
await user.click(storageReplicasInput);
|
||||
await user.clear(storageReplicasInput);
|
||||
await user.type(storageReplicasInput, '4');
|
||||
await clickOnElement(storageReplicasInput);
|
||||
await userClearElement(storageReplicasInput);
|
||||
await userType(storageReplicasInput, '4');
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /storage vcpu/i }),
|
||||
@@ -484,7 +427,7 @@ test('should take replicas into account when confirming the resources', async ()
|
||||
5 * RESOURCE_MEMORY_MULTIPLIER,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
|
||||
@@ -515,3 +458,50 @@ test('should take replicas into account when confirming the resources', async ()
|
||||
expect(within(dialog).getByText(/\$0.0270\/min/i)).toBeInTheDocument();
|
||||
expect(within(dialog).getByText(/\$1125\.00\/mo/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should validate if vCPU and Memory match the 1:2 ratio if more than 1 replica is selected', async () => {
|
||||
render(<ResourcesForm />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('slider', { name: /total available vcpu/i }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', {
|
||||
name: /total available vcpu/i,
|
||||
}),
|
||||
20 * RESOURCE_VCPU_MULTIPLIER,
|
||||
);
|
||||
|
||||
const storageReplicasInput = screen.getAllByPlaceholderText('Replicas')[2];
|
||||
await clickOnElement(storageReplicasInput);
|
||||
await userClearElement(storageReplicasInput);
|
||||
await userType(storageReplicasInput, '2');
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /storage vcpu/i }),
|
||||
1 * RESOURCE_VCPU_MULTIPLIER,
|
||||
);
|
||||
|
||||
changeSliderValue(
|
||||
screen.getByRole('slider', { name: /storage memory/i }),
|
||||
6 * RESOURCE_MEMORY_MULTIPLIER,
|
||||
);
|
||||
|
||||
await clickOnElement(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(screen.getByText(/invalid configuration/i)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/please check the form for errors and the allocation for each service and try again\./i,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
const validationErrorMessage = screen.getByText(
|
||||
/vCPU and Memory for this service must follow a 1:2 ratio when more than one replica is selected or when the autoscaler is activated\./i,
|
||||
);
|
||||
expect(validationErrorMessage).toBeInTheDocument();
|
||||
expect(validationErrorMessage).toHaveStyle({ color: '#D32F2F' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,9 @@ import type {
|
||||
import {
|
||||
render as rtlRender,
|
||||
waitForElementToBeRemoved as rtlWaitForElementToBeRemoved,
|
||||
waitFor,
|
||||
} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime';
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
@@ -160,5 +162,26 @@ export const mockPointerEvent = () => {
|
||||
window.HTMLElement.prototype.hasPointerCapture = vi.fn();
|
||||
};
|
||||
|
||||
export async function clickOnElement(element: Element) {
|
||||
const user = userEvent.setup();
|
||||
await waitFor(async () => {
|
||||
await user.click(element);
|
||||
});
|
||||
}
|
||||
|
||||
export async function userType(element: Element, value: string, options?: any) {
|
||||
const user = userEvent.setup();
|
||||
await waitFor(async () => {
|
||||
await user.type(element, value, options);
|
||||
});
|
||||
}
|
||||
|
||||
export async function userClearElement(element: Element) {
|
||||
const user = userEvent.setup();
|
||||
await waitFor(async () => {
|
||||
await user.clear(element);
|
||||
});
|
||||
}
|
||||
|
||||
export * from '@testing-library/react';
|
||||
export { render, waitForElementToBeRemoved };
|
||||
|
||||
@@ -12,5 +12,8 @@ export default defineConfig({
|
||||
globals: true,
|
||||
setupFiles: 'src/setupTests.ts',
|
||||
include: ['src/**/*.(spec|test).{js,jsx,ts,tsx}'],
|
||||
deps: {
|
||||
inline: ['clsx'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -31,9 +31,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.3.4",
|
||||
"@next/eslint-plugin-next": "14.2.26",
|
||||
"@types/node": "^16.18.93",
|
||||
"@types/react": "^18.2.73",
|
||||
"@xstate/inspect": "^0.6.5",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-next": "12.0.10",
|
||||
"typescript": "^4.9.5",
|
||||
"ws": "^8.16.0",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/webfontloader": "^1.6.38",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@xstate/inspect": "^0.6.5",
|
||||
"sass": "1.32.0",
|
||||
"sass": "1.86.1",
|
||||
"typescript": "4.9.4",
|
||||
"vite": "^5.4.15",
|
||||
"vue-tsc": "^0.38.9"
|
||||
|
||||
13
examples/vue-quickstart/.eslintrc
Normal file
13
examples/vue-quickstart/.eslintrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["../../config/.eslintrc.vue.js", "@antfu"],
|
||||
"rules": {
|
||||
"@typescript-eslint/comma-dangle": "off",
|
||||
"curly": "off",
|
||||
"quote-props": "off",
|
||||
"vue/html-self-closing": "off",
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"eol-last": "off",
|
||||
"eslint-comments/no-unlimited-disable": "off"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['../../config/.eslintrc.vue.js', '@antfu'],
|
||||
rules: {
|
||||
'@typescript-eslint/comma-dangle': 'off',
|
||||
curly: 'off',
|
||||
'quote-props': 'off',
|
||||
'vue/html-self-closing': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'eol-last': 'off',
|
||||
'eslint-comments/no-unlimited-disable': 'off'
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-quickstart",
|
||||
"type": "module",
|
||||
"version": "0.4.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -25,13 +26,13 @@
|
||||
"@antfu/eslint-config": "^0.23.1",
|
||||
"@iconify-json/carbon": "^1.1.31",
|
||||
"@types/node": "^16.18.93",
|
||||
"@unocss/reset": "^0.33.5",
|
||||
"@unocss/reset": "66.0.0",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"eslint": "^8.57.0",
|
||||
"jsdom": "^19.0.0",
|
||||
"typescript": "^4.9.5",
|
||||
"unocss": "^0.33.5",
|
||||
"unocss": "66.0.0",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.4.15",
|
||||
|
||||
@@ -173,7 +173,9 @@
|
||||
"cross-spawn": "^7.0.5",
|
||||
"cookie@<0.7.0": ">=0.7.0",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
"axios@<1.8.2": ">=1.8.2"
|
||||
"axios@<1.8.2": ">=1.8.2",
|
||||
"vite@>=5.0.0 <5.4.16": ">=5.4.16",
|
||||
"vite@>=6.2.0 <6.2.4": ">=6.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1462
pnpm-lock.yaml
generated
1462
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user