[ENG-1624] E2E Tests (#2192)

* Basic cypress test

* Add e2e test command

* Add full e2e test for creating and deleting a library

* Add cypress to CI

* Fix cypress CI
 - Use arm macos for running cypress
 - Setup rust on cypress job
 - Increase cypress CI timeout

* Remove unused APPLE_SIGNING_IDENTITY envvar

* Update deps again + Try to fix Cypress CI setup

* Update deps again

* Only test default location if it exists

* Use latest v18 NodeJS in CI

* Increase minimum node version due to updated dependency
 - @typescript-eslint/eslint-plugin now requires node >=18.18

* Fix testing failing for optional default locations

* Enable video recording
 - Fix screenshot and video artifact uploading

* Fix location test
 - Use correct location name, instead of trying to derive it from the internal id
This commit is contained in:
Vítor Vasconcellos 2024-03-19 17:22:17 -03:00 committed by GitHub
parent 16354b0f72
commit 08c42a36fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 3909 additions and 3230 deletions

View file

@ -48,6 +48,51 @@ jobs:
- name: Perform linting
run: pnpm lint
cypress:
name: Cypress
runs-on: macos-14
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup System and Rust
uses: ./.github/actions/setup-system
with:
token: ${{ secrets.GITHUB_TOKEN }}
target: aarch64-apple-darwin
- name: Setup Node.js, pnpm and dependencies
uses: ./.github/actions/setup-pnpm
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Cypress
uses: cypress-io/github-action@v6
with:
runTests: false
working-directory: .
- name: E2E test
uses: cypress-io/github-action@v6
with:
install: false
command: pnpm test:e2e
working-directory: apps/web
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-screenshots
path: apps/web/cypress/screenshots
if-no-files-found: ignore
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: apps/web/cypress/videos
if-no-files-found: ignore
rustfmt:
name: Rust Formatting
runs-on: ubuntu-latest

2
.nvmrc
View file

@ -1 +1 @@
v18.17
v18

1
Cargo.lock generated
View file

@ -8606,6 +8606,7 @@ dependencies = [
"mime_guess",
"rspc",
"sd-core",
"tempfile",
"tokio",
"tracing",
]

View file

@ -14,7 +14,7 @@
"dependencies": {
"@oscartbeaumont-sd/rspc-client": "=0.0.0-main-dc31e5b2",
"@oscartbeaumont-sd/rspc-tauri": "=0.0.0-main-dc31e5b2",
"@remix-run/router": "^1.13.1",
"@remix-run/router": "=1.13.1",
"@sd/client": "workspace:*",
"@sd/interface": "workspace:*",
"@sd/ui": "workspace:*",
@ -25,18 +25,18 @@
"immer": "^10.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.1",
"react-router-dom": "=6.20.1",
"sonner": "^1.0.3"
},
"devDependencies": {
"@sd/config": "workspace:*",
"@sentry/vite-plugin": "^2.14.2",
"@tauri-apps/cli": "^1.5.10",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"sass": "^1.71.1",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vite-tsconfig-paths": "^4.3.1"
"@sentry/vite-plugin": "^2.16.0",
"@tauri-apps/cli": "^1.5.11",
"@types/react": "^18.2.67",
"@types/react-dom": "^18.2.22",
"sass": "^1.72.0",
"typescript": "^5.4.2",
"vite": "^5.1.6",
"vite-tsconfig-paths": "^4.3.2"
}
}

View file

@ -56,15 +56,15 @@
"@octokit/openapi-types": "^20.0.0",
"@sd/config": "workspace:*",
"@svgr/webpack": "^8.1.0",
"@types/node": "~18.17.19",
"@types/react": "^18.2.61",
"@types/node": ">18.x",
"@types/react": "^18.2.67",
"@types/react-burger-menu": "^2.8.7",
"@types/react-dom": "^18.2.19",
"@types/react-dom": "^18.2.22",
"@types/three": "^0.162.0",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.36",
"sharp": "^0.33.2",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View file

@ -74,13 +74,13 @@
"zod": "~3.22.4"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/core": "^7.24.0",
"@rnx-kit/metro-config": "^1.3.15",
"@sd/config": "workspace:*",
"@types/react": "^18.2.61",
"@types/react": "^18.2.67",
"babel-plugin-module-resolver": "^5.0.0",
"eslint-plugin-react-native": "^4.1.0",
"react-native-svg-transformer": "^1.3.0",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View file

@ -22,5 +22,7 @@ rspc = { workspace = true, features = ["axum"] }
tokio = { workspace = true, features = ["sync", "rt-multi-thread", "signal"] }
tracing = { workspace = true }
tempfile = "3.10.1"
include_dir = "0.7.3"
mime_guess = "2.0.4"

View file

@ -21,7 +21,13 @@ async fn main() {
}
#[cfg(debug_assertions)]
{
Path::new(env!("CARGO_MANIFEST_DIR")).join("sdserver_data")
if env::var("E2E_TEST").is_ok() {
let temp_dir =
tempfile::tempdir().expect("Tempdir for e2e test must be created!");
temp_dir.into_path()
} else {
Path::new(env!("CARGO_MANIFEST_DIR")).join("sdserver_data")
}
}
}
};

View file

@ -14,13 +14,11 @@ const config: StorybookConfig = {
}
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
{
name: '@storybook/addon-styling'
}
],
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-styling-webpack'
],
framework: {
name: '@storybook/react-vite',
options: {}

View file

@ -6,13 +6,13 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-interactions": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^7.6.17",
"@storybook/react": "^7.6.17",
"@storybook/react-vite": "^7.6.17",
"@storybook/addon-essentials": "^8.0.1",
"@storybook/addon-interactions": "^8.0.1",
"@storybook/addon-links": "^8.0.1",
"@storybook/addon-styling-webpack": "^1.0.0",
"@storybook/blocks": "^8.0.1",
"@storybook/react": "^8.0.1",
"@storybook/react-vite": "^8.0.1",
"@storybook/testing-library": "^0.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@ -20,16 +20,16 @@
"devDependencies": {
"@sd/config": "workspace:*",
"@sd/ui": "workspace:*",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"@types/react": "^18.2.67",
"@types/react-dom": "^18.2.22",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.36",
"postcss-pseudo-companion-classes": "^0.1.1",
"prop-types": "^15.8.1",
"rollup-plugin-node-builtins": "^2.1.2",
"storybook": "^7.6.17",
"storybook": "^8.0.1",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
"vite": "^5.1.4"
"typescript": "^5.4.2",
"vite": "^5.1.6"
}
}

View file

@ -4,5 +4,5 @@ module.exports = {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
},
ignorePatterns: ['playwright.config.ts', 'tests/**/*']
ignorePatterns: ['playwright.config.ts', 'tests/**/*', 'cypress/**/*']
};

View file

@ -0,0 +1,11 @@
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:8002',
setupNodeEvents(on, config) {
// implement node event listeners here
}
},
video: true
});

View file

@ -0,0 +1,8 @@
module.exports = {
extends: [require.resolve('@sd/config/eslint/web.js')],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
},
plugins: ['cypress']
};

2
apps/web/cypress/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
screenshots
videos

View file

@ -0,0 +1,177 @@
import { discord, libraryName } from '../fixtures/onboarding.json';
describe('Onboarding', () => {
// TODO: Create debug flag to bypass auto language detection
it('Alpha onboarding', () => {
cy.visit('/', {
onBeforeLoad(win) {
cy.stub(win, 'open').as('winOpen');
}
});
// Check redirect to initial alpha onboarding screen
cy.url().should('contain', '/onboarding/alpha');
// Check application name is present
cy.get('h1').should('contain', 'Spacedrive');
// Check logo image exists and loaded correctly
cy.get('img[alt="Spacedrive"]')
.should('be.visible')
.and('have.prop', 'naturalWidth')
.should('be.greaterThan', 0);
// Check we are in the alpha release screen
cy.get('h1').should('contain', 'Alpha Release');
// Check Join Discord button exists and point to a valid discord invite
cy.get('button').contains('Join Discord').click();
cy.get('@winOpen').should('be.calledWith', discord);
// Check we have a button to continue to the Library creation
cy.get('a')
.contains('Continue')
.should('have.attr', 'href', '/onboarding/new-library')
.click();
// Check we were redirect to Library creation screen
cy.url().should('contain', '/onboarding/new-library');
// Check create library screen title
cy.get('h2').should('contain', 'Create a Library');
// Check we have a button to create a new library
cy.get('button').contains('New library').as('newLibraryButton');
// Check we have an input to set the library name
cy.get('input[placeholder="e.g. \\"James\' Library\\""]').as('libraryNameInput');
// Check newLibraryButton is disabled
cy.get('@newLibraryButton').should('be.disabled');
// Get input with placeholder 'e.g. "James' Library"'
cy.get('@libraryNameInput').type(libraryName);
// Check newLibraryButton is enabled
cy.get('@newLibraryButton').should('be.enabled');
// Check we can clear the input and the button is disabled again
cy.get('@libraryNameInput').clear();
cy.get('@newLibraryButton').should('be.disabled');
cy.get('@libraryNameInput').type(libraryName);
// Check we have a button to continue to the add default locations screen
cy.get('button').contains('New library').click();
// Check redirect to add default locations
cy.url().should('contain', '/onboarding/locations');
// Check we have a Toggle All button
cy.get('#toggle-all').as('toggleAllButton');
cy.get('[data-locations]').then((locationsElem) => {
const locations = locationsElem.data('locations');
if (locations == null || typeof locations !== 'object')
throw new Error('Invalid locations data');
// Check that default location checkboxes work
for (const state of ['unchecked', 'checked']) {
if (state === 'checked') {
// Check if @toggleAllButton has data-state == checked
cy.get('@toggleAllButton').should('have.attr', 'data-state', 'checked');
// Uncheck all locations
cy.get('@toggleAllButton').click();
}
// Check we have all the default locations available
for (const [location, locationName] of Object.entries(locations)) {
if (typeof locationName !== 'string') throw new Error('Invalid location name');
let newState: typeof state;
if (state === 'unchecked') {
cy.get('label').contains(locationName).click();
newState = 'checked';
} else {
newState = 'unchecked';
}
cy.get(`button[id="locations.${location}"]`).should(
'have.attr',
'data-state',
newState
);
}
}
});
// Check we have a button to continue to the privacy screen
cy.get('button').contains('Continue').click();
// Check redirect to privacy screen
cy.url().should('contain', '/onboarding/privacy');
// Check privacy screen title
cy.get('h2').should('contain', 'Your Privacy');
// Check we have all privacy options
cy.get('label').contains('Share the bare minimum').click();
cy.get('#radiominimal-telemetry').should('have.attr', 'data-state', 'checked');
cy.get('#radioshare-telemetry').should('have.attr', 'data-state', 'unchecked');
cy.get('label').contains('Share anonymous usage').click();
cy.get('#radioshare-telemetry').should('have.attr', 'data-state', 'checked');
cy.get('#radiominimal-telemetry').should('have.attr', 'data-state', 'unchecked');
// Check More info button exists and point to the valid pravacy policy
cy.get('button').contains('More info').click();
cy.get('@winOpen').should(
'be.calledWith',
'https://www.spacedrive.com/docs/product/resources/privacy'
);
// Check we have a button to finish onboarding
cy.get('button[type="submit"]').contains('Continue').click();
// Check redirect to privacy screen
cy.url().should('contain', '/onboarding/creating-library');
// FIX-ME: This fails a lot, due to the creating library screen only being show for a short time
// Check creating library screen title
// cy.get('h2').should('contain', 'Creating your library');
// Check redirect to Library
cy.url().should((url) => {
expect(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\//.test(url)).to
.be.true;
});
// Click on the library submenu
cy.get('button[aria-haspopup="menu"]').contains(libraryName).click();
cy.get('a').contains('Manage Library').click();
// Check redirect to Library settings
cy.url().should('contain', '/settings/library/general');
// Check Library seetings screen title
cy.get('h1').should('contain', 'Library Settings');
// Check Library name is correct
cy.get('label')
.contains('Name')
.parent()
.find('input')
.should((input) => {
expect(input.val()).to.eq(libraryName);
});
// Delete Library
cy.get('button').contains('Delete').click();
// Check confirmation modal for deleting appears
cy.get('body > div[role="dialog"]').as('deleteModal');
// Check modal title
cy.get('@deleteModal').find('h2').should('contain', 'Delete Library');
// Confirm delete
cy.get('@deleteModal').find('button').contains('Delete').click();
});
});

View file

@ -0,0 +1,4 @@
{
"discord": "https://discord.gg/ukRnWSnAbG",
"libraryName": "Test Library"
}

View file

@ -0,0 +1,37 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }

View file

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"],
"resolveJsonModule": true,
},
"include": ["**/*.ts"]
}

View file

@ -3,10 +3,15 @@
"private": true,
"type": "module",
"scripts": {
"dev:api": "env E2E_TEST=1 cargo run -p sd-server",
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "VITE_SD_DEMO_MODE=true playwright test",
"test:e2e": "env WAIT_ON_TIMEOUT=1800000 start-test dev:api http://localhost:8080 dev http://localhost:8002 cy:run",
"test:interactive": "env WAIT_ON_TIMEOUT=1800000 start-test dev:api http://localhost:8080 dev http://localhost:8002 cy:open",
"cy:run": "env ELECTRON_EXTRA_LAUNCH_ARGS=--lang=en cypress run",
"cy:open": "env ELECTRON_EXTRA_LAUNCH_ARGS=--lang=en cypress open --e2e",
"typecheck": "tsc -b",
"lint": "eslint src --cache"
},
@ -19,19 +24,22 @@
"html2canvas": "^1.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.1"
"react-router-dom": "=6.20.1"
},
"devDependencies": {
"@playwright/test": "^1.42.0",
"@playwright/test": "^1.42.1",
"@sd/config": "workspace:*",
"@sd/ui": "workspace:*",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"@types/react": "^18.2.67",
"@types/react-dom": "^18.2.22",
"autoprefixer": "^10.4.18",
"cypress": "^13.7.0",
"eslint-plugin-cypress": "^2.15.1",
"postcss": "^8.4.36",
"rollup-plugin-visualizer": "^5.12.0",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vite-tsconfig-paths": "^4.3.1"
"start-server-and-test": "^2.0.3",
"typescript": "^5.4.2",
"vite": "^5.1.6",
"vite-tsconfig-paths": "^4.3.2"
}
}

View file

@ -17,7 +17,7 @@
},
"devDependencies": {
"@tanstack/react-query": "^4.36.1",
"typescript": "^5.3.3",
"typescript": "^5.4.2",
"vite": "^5.0.10",
"tailwindcss": "^3.3.3"
}

View file

@ -69,6 +69,7 @@ export default function OnboardingLocations() {
}),
{} as Record<SystemLocation, string>
);
return Object.keys(locations).length > 0 ? locations : null;
}, [data]);
@ -131,7 +132,10 @@ export default function OnboardingLocations() {
}}
/>
<div className="grid grid-cols-2 gap-2">
<div
className="grid grid-cols-2 gap-2"
data-locations={JSON.stringify(systemLocations)}
>
{(Object.keys(systemLocations) as SystemLocation[]).map((location) => (
<Controller
key={location}

View file

@ -21,7 +21,7 @@
"@radix-ui/react-toast": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.2",
"@redux-devtools/extension": "^3.2.5",
"@remix-run/router": "^1.13.1",
"@remix-run/router": "=1.13.1",
"@sd/assets": "workspace:*",
"@sd/client": "workspace:*",
"@sd/ui": "workspace:*",
@ -51,8 +51,8 @@
"react-json-view": "^1.21.3",
"react-loading-skeleton": "^3.3.1",
"react-markdown": "^9.0.0",
"react-router": "^6.20.1",
"react-router-dom": "^6.20.1",
"react-router": "=6.20.1",
"react-router-dom": "=6.20.1",
"react-selecto": "^1.26.0",
"react-slidedown": "^2.4.7",
"react-sticky-el": "^2.1.0",
@ -70,14 +70,14 @@
},
"devDependencies": {
"@sd/config": "workspace:*",
"@types/node": "~18.17.19",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"@types/node": ">18.x",
"@types/react": "^18.2.67",
"@types/react-dom": "^18.2.22",
"@types/uuid": "^9.0.8",
"tailwindcss": "^3.4.1",
"type-fest": "^4.10.3",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"type-fest": "^4.12.0",
"typescript": "^5.4.2",
"vite": "^5.1.6",
"vite-plugin-svgr": "^3.3.0"
}
}

View file

@ -29,7 +29,7 @@
"typecheck": "pnpm -r typecheck",
"lint": "turbo run lint",
"lint:fix": "turbo run lint -- --fix",
"clean": "git clean -qfX ."
"clean": "cargo clean; git clean -qfX ."
},
"pnpm": {
"patchedDependencies": {
@ -37,32 +37,32 @@
"@contentlayer/cli@0.3.4": "patches/@contentlayer__cli@0.3.4.patch"
},
"overrides": {
"@types/node": "~18.19.21",
"@types/node": ">18.x",
"react-router": "=6.20.1",
"react-router-dom": "=6.20.1",
"@remix-run/router": "=1.13.1",
"@contentlayer/cli": "=0.3.4",
"@typescript-eslint/parser": "^7.1.0"
"@typescript-eslint/parser": "^7.1.1"
}
},
"devDependencies": {
"@babel/plugin-syntax-import-assertions": "^7.23.3",
"@cspell/dict-rust": "^4.0.2",
"@cspell/dict-typescript": "^3.1.2",
"@ianvs/prettier-plugin-sort-imports": "^4.1.1",
"cspell": "^8.4.1",
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"cspell": "^8.6.0",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"turbo": "^1.12.4",
"turbo-ignore": "^1.12.4",
"typescript": "^5.3.3",
"vite": "^5.1.4"
"prettier-plugin-tailwindcss": "^0.5.12",
"turbo": "^1.12.5",
"turbo-ignore": "^1.12.5",
"typescript": "^5.4.2",
"vite": "^5.1.6"
},
"engines": {
"pnpm": ">=8.0.0",
"npm": "pnpm",
"yarn": "pnpm",
"node": ">=18.17 <19 || >=20.1"
"node": ">=18.18 <19 || >=20.1"
},
"eslintConfig": {
"root": true

View file

@ -27,8 +27,8 @@
},
"devDependencies": {
"@sd/config": "workspace:*",
"@types/react": "^18.2.61",
"typescript": "^5.3.3"
"@types/react": "^18.2.67",
"typescript": "^5.4.2"
},
"peerDependencies": {
"react": "^18.2"

View file

@ -11,24 +11,24 @@
"lint": "eslint . --cache"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@vitejs/plugin-react-swc": "^3.6.0",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.0",
"eslint-config-next": "^14.1.3",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^1.12.4",
"eslint-config-turbo": "^1.12.5",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-solid": "^0.13.1",
"eslint-plugin-tailwindcss": "^3.14.3",
"eslint-plugin-tailwindcss": "^3.15.1",
"eslint-utils": "^3.0.0",
"regexpp": "^3.2.0",
"vite-plugin-html": "^3.2.2",
"vite-plugin-i18next-loader": "^2.0.12",
"vite-plugin-inspect": "^0.8.3",
"vite-plugin-solid": "^2.10.1",
"vite-plugin-solid": "^2.10.2",
"vite-plugin-svgr": "^3.3.0"
},
"dependencies": {

View file

@ -40,27 +40,27 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-loading-icons": "^1.1.0",
"react-router-dom": "^6.20.1",
"react-router-dom": "=6.20.1",
"sonner": "^1.0.3",
"use-debounce": "^9.0.4",
"zod": "~3.22.4"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/core": "^7.24.0",
"@headlessui/tailwindcss": "^0.2.0",
"@sd/config": "workspace:*",
"@storybook/types": "^7.6.17",
"@storybook/types": "^8.0.1",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@types/node": "~18.17.19",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"sass": "^1.71.1",
"@types/node": ">18.x",
"@types/react": "^18.2.67",
"@types/react-dom": "^18.2.22",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.36",
"sass": "^1.72.0",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-radix": "^2.8.0",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,21 +22,21 @@
"archive-wasm": "^1.6.1",
"mustache": "^4.2.0",
"semver": "^7.6.0",
"undici": "^6.6.2"
"undici": "^6.9.0"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/core": "^7.24.0",
"@babel/eslint-parser": "^7.23.10",
"@babel/eslint-plugin": "^7.23.5",
"@types/mustache": "^4.2.5",
"@types/node": "~18.17.19",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@types/node": ">18.x",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-jsdoc": "^48.2.0",
"eslint-plugin-jsdoc": "^48.2.1",
"eslint-plugin-prettier": "^5.1.3",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}