mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 12:13:27 +00:00
[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:
parent
16354b0f72
commit
08c42a36fe
45
.github/workflows/ci.yml
vendored
45
.github/workflows/ci.yml
vendored
|
@ -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
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -8606,6 +8606,7 @@ dependencies = [
|
|||
"mime_guess",
|
||||
"rspc",
|
||||
"sd-core",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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: {}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ module.exports = {
|
|||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
},
|
||||
ignorePatterns: ['playwright.config.ts', 'tests/**/*']
|
||||
ignorePatterns: ['playwright.config.ts', 'tests/**/*', 'cypress/**/*']
|
||||
};
|
||||
|
|
11
apps/web/cypress.config.ts
Normal file
11
apps/web/cypress.config.ts
Normal 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
|
||||
});
|
8
apps/web/cypress/.eslintrc.cjs
Normal file
8
apps/web/cypress/.eslintrc.cjs
Normal 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
2
apps/web/cypress/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
screenshots
|
||||
videos
|
177
apps/web/cypress/e2e/onboarding.spec.cy.ts
Normal file
177
apps/web/cypress/e2e/onboarding.spec.cy.ts
Normal 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();
|
||||
});
|
||||
});
|
4
apps/web/cypress/fixtures/onboarding.json
Normal file
4
apps/web/cypress/fixtures/onboarding.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"discord": "https://discord.gg/ukRnWSnAbG",
|
||||
"libraryName": "Test Library"
|
||||
}
|
37
apps/web/cypress/support/commands.ts
Normal file
37
apps/web/cypress/support/commands.ts
Normal 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>
|
||||
// }
|
||||
// }
|
||||
// }
|
20
apps/web/cypress/support/e2e.ts
Normal file
20
apps/web/cypress/support/e2e.ts
Normal 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')
|
9
apps/web/cypress/tsconfig.json
Normal file
9
apps/web/cypress/tsconfig.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress", "node"],
|
||||
"resolveJsonModule": true,
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
22
package.json
22
package.json
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
6607
pnpm-lock.yaml
6607
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue