mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
feat: upgrade react 18 (#3495)
* chore: update react 18 * Create four-ways-try.md * Update signin.cy.ts * chore: new ci * Update e2e-ui.yml * Update e2e-ui.yml * ci * ci * ci * Update e2e-ui.yml * Update e2e-ui.yml * chore: fix ui test * Update publish.cy.ts * chore: update tests * add strict mode
This commit is contained in:
parent
17984fa31b
commit
0481b9a329
30 changed files with 566 additions and 572 deletions
5
.changeset/four-ways-try.md
Normal file
5
.changeset/four-ways-try.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@verdaccio/ui-theme': minor
|
||||
---
|
||||
|
||||
feat: upgrade to react 18
|
47
.github/workflows/ci.yml
vendored
47
.github/workflows/ci.yml
vendored
|
@ -33,7 +33,9 @@ jobs:
|
|||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- name: set store
|
||||
run: |
|
||||
mkdir ~/.pnpm-store
|
||||
|
@ -58,7 +60,9 @@ jobs:
|
|||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- uses: actions/cache@1c73980b09e7aea7201f325a7aa3ad00beddcdda # tag=v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
|
@ -78,7 +82,9 @@ jobs:
|
|||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- uses: actions/cache@1c73980b09e7aea7201f325a7aa3ad00beddcdda # tag=v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
|
@ -87,7 +93,7 @@ jobs:
|
|||
run: pnpm recursive install --offline --frozen-lockfile --reporter=silence --ignore-scripts
|
||||
- name: Lint
|
||||
run: pnpm format:check
|
||||
build:
|
||||
test:
|
||||
needs: [format, lint]
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
@ -103,7 +109,9 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- uses: actions/cache@1c73980b09e7aea7201f325a7aa3ad00beddcdda # tag=v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
|
@ -114,31 +122,8 @@ jobs:
|
|||
run: pnpm build
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
ci-e2e-ui:
|
||||
needs: [format, lint]
|
||||
runs-on: ubuntu-latest
|
||||
name: UI Test E2E
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
- uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # tag=v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
- uses: actions/cache@1c73980b09e7aea7201f325a7aa3ad00beddcdda # tag=v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
- name: Install
|
||||
run: pnpm recursive install --offline --frozen-lockfile --reporter=silence --registry http://localhost:4873
|
||||
- name: build
|
||||
run: pnpm build
|
||||
- name: Test UI
|
||||
run: pnpm test:e2e:ui
|
||||
# env:
|
||||
# DEBUG: verdaccio:e2e*
|
||||
sync-translations:
|
||||
needs: [ci-e2e-ui]
|
||||
needs: [test]
|
||||
runs-on: ubuntu-latest
|
||||
name: synchronize translations
|
||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'workflow_dispatch'
|
||||
|
@ -148,7 +133,9 @@ jobs:
|
|||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@6.32.15 -g
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- uses: actions/cache@1c73980b09e7aea7201f325a7aa3ad00beddcdda # tag=v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
|
|
36
.github/workflows/e2e-ui.yml
vendored
Normal file
36
.github/workflows/e2e-ui.yml
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
name: E2E UI
|
||||
|
||||
on: [pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: UI Test E2E
|
||||
services:
|
||||
verdaccio:
|
||||
image: verdaccio/verdaccio:5
|
||||
ports:
|
||||
- 4873:4873
|
||||
env:
|
||||
NODE_ENV: production
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
- name: Use Node
|
||||
uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # tag=v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install pnpm
|
||||
run: |
|
||||
corepack enable
|
||||
corepack prepare --activate pnpm@6.32.15
|
||||
- name: Install
|
||||
run: pnpm install --frozen-lockfile --reporter=silence --registry http://localhost:4873
|
||||
- name: build
|
||||
run: pnpm build
|
||||
- name: Test UI
|
||||
run: pnpm test:e2e:ui
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: videos
|
||||
path: /home/runner/work/verdaccio/verdaccio/e2e/ui/cypress/videos
|
1
.npmrc
1
.npmrc
|
@ -1,5 +1,4 @@
|
|||
always-auth = true
|
||||
recursive-install = true
|
||||
registry = https://registry.verdaccio.org
|
||||
loglevel=info
|
||||
fetch-retries="10"
|
||||
|
|
|
@ -3,18 +3,26 @@ web:
|
|||
title: verdaccio-server-e2e
|
||||
login: true
|
||||
|
||||
log: { type: stdout, format: pretty, level: debug }
|
||||
log: { type: stdout, format: json, level: info }
|
||||
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
|
||||
auth:
|
||||
htpasswd:
|
||||
file: ./htpasswd
|
||||
|
||||
packages:
|
||||
'@verdaccio/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
'**':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
|
||||
proxy: npmjs
|
||||
_debug: true
|
||||
|
|
|
@ -29,8 +29,8 @@ export default defineConfig({
|
|||
});
|
||||
|
||||
on('task', {
|
||||
publishScoped() {
|
||||
const scopedPackageMetadata = generatePackageMetadata('pkg-scoped', '1.0.6');
|
||||
publishScoped({ pkgName }) {
|
||||
const scopedPackageMetadata = generatePackageMetadata(pkgName, '1.0.6');
|
||||
const server = new ServerQuery(registry1.getRegistryUrl());
|
||||
server
|
||||
.putPackage(scopedPackageMetadata.name, scopedPackageMetadata, {
|
||||
|
|
|
@ -5,18 +5,19 @@ describe('publish spec', () => {
|
|||
// @ts-expect-error
|
||||
const registry = await cy.task('registry');
|
||||
ctx.url = registry.registryUrl;
|
||||
const pkgName = `@verdaccio/pkg-scoped`;
|
||||
cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');
|
||||
cy.intercept('GET', '/-/verdaccio/data/packages').as('pkgs');
|
||||
cy.intercept('GET', '/-/verdaccio/data/sidebar/pkg-scoped').as('sidebar');
|
||||
cy.intercept('GET', '/-/verdaccio/data/package/readme/pkg-scoped').as('readme');
|
||||
cy.task('publishScoped', { pkgName: 'pkg-protected' });
|
||||
cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}`).as('sidebar');
|
||||
cy.intercept('GET', `/-/verdaccio/data/package/readme/${pkgName}`).as('readme');
|
||||
cy.task('publishScoped', { pkgName });
|
||||
});
|
||||
|
||||
it('should have one published package', () => {
|
||||
cy.visit(ctx.url);
|
||||
cy.login(credentials.user, credentials.password);
|
||||
cy.wait('@sign');
|
||||
cy.getByTestId('package-title').should('have.length', 1);
|
||||
// cy.getByTestId('package-title').should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should navigate to page detail', () => {
|
||||
|
@ -25,9 +26,7 @@ describe('publish spec', () => {
|
|||
cy.wait('@sign');
|
||||
cy.wait('@pkgs');
|
||||
cy.wait(300);
|
||||
cy.getByTestId('package-title').click();
|
||||
cy.wait('@sidebar');
|
||||
cy.wait('@readme');
|
||||
cy.getByTestId('package-title').first().click();
|
||||
});
|
||||
|
||||
it('should have readme content', () => {
|
||||
|
@ -35,9 +34,9 @@ describe('publish spec', () => {
|
|||
cy.login(credentials.user, credentials.password);
|
||||
cy.wait('@sign');
|
||||
cy.wait('@pkgs');
|
||||
cy.getByTestId('package-title').click();
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('package-title').first().click();
|
||||
cy.wait('@readme');
|
||||
cy.wait('@sidebar');
|
||||
cy.get('.markdown-body').should('have.length', 1);
|
||||
cy.contains('.markdown-body', /test/);
|
||||
});
|
||||
|
@ -48,9 +47,9 @@ describe('publish spec', () => {
|
|||
cy.wait('@sign');
|
||||
cy.wait('@pkgs');
|
||||
cy.wait(300);
|
||||
cy.getByTestId('package-title').click();
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('package-title').first().click();
|
||||
cy.wait('@readme');
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('dependencies-tab').click();
|
||||
cy.wait(100);
|
||||
cy.getByTestId('dependencies').should('have.length', 1);
|
||||
|
@ -67,9 +66,9 @@ describe('publish spec', () => {
|
|||
cy.wait('@sign');
|
||||
cy.wait('@pkgs');
|
||||
cy.wait(300);
|
||||
cy.getByTestId('package-title').click();
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('package-title').first().click();
|
||||
cy.wait('@readme');
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('versions-tab').click();
|
||||
cy.getByTestId('tag-latest').children().invoke('text').should('match', /1.0.6/);
|
||||
cy.screenshot();
|
||||
|
@ -81,9 +80,9 @@ describe('publish spec', () => {
|
|||
cy.wait('@sign');
|
||||
cy.wait('@pkgs');
|
||||
cy.wait(300);
|
||||
cy.getByTestId('package-title').click();
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('package-title').first().click();
|
||||
cy.wait('@readme');
|
||||
cy.wait('@sidebar');
|
||||
cy.getByTestId('uplinks-tab').click();
|
||||
cy.getByTestId('no-uplinks').should('be.visible');
|
||||
cy.screenshot();
|
||||
|
|
|
@ -25,6 +25,7 @@ describe('sign spec', () => {
|
|||
cy.wait(100);
|
||||
cy.getByTestId('logOutDialogIcon').click();
|
||||
cy.screenshot();
|
||||
cy.wait(200);
|
||||
cy.getByTestId('header--button-login').contains('Login');
|
||||
cy.screenshot();
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"@verdaccio/config": "workspace:6.0.0-6-next.50",
|
||||
"@verdaccio/test-helper": "workspace:2.0.0-6-next.6",
|
||||
"debug": "4.3.4",
|
||||
"cypress": "10.10.0"
|
||||
"cypress": "11.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"cypress:open": "cypress open",
|
||||
|
|
|
@ -6,4 +6,22 @@ export const handlers = [
|
|||
rest.get('http://localhost:9000/-/verdaccio/data/packages', (req, res, ctx) => {
|
||||
return res(ctx.json(packagesPayload));
|
||||
}),
|
||||
|
||||
rest.post<{ username: string; password: string }, { token: string; username: string }>(
|
||||
'http://localhost:9000/-/verdaccio/sec/login',
|
||||
// @ts-ignore
|
||||
async (req, res, ctx) => {
|
||||
const body = await req.json();
|
||||
if (body.username === 'fail') {
|
||||
return ctx.status(401);
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.json({
|
||||
username: body.username,
|
||||
token: 'valid token',
|
||||
})
|
||||
);
|
||||
}
|
||||
),
|
||||
];
|
||||
|
|
|
@ -13,30 +13,30 @@
|
|||
"homepage": "https://verdaccio.org",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"@types/react": "17.0.50",
|
||||
"@types/react-autosuggest": "10.1.5",
|
||||
"@types/react-dom": "17.0.17",
|
||||
"@types/react-helmet": "6.1.5",
|
||||
"@types/redux": "3.6.0",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-virtualized": "9.21.21",
|
||||
"@emotion/react": "11.10.4",
|
||||
"@emotion/jest": "11.10.0",
|
||||
"@emotion/styled": "11.10.4",
|
||||
"@emotion/css": "11.10.0",
|
||||
"@emotion/babel-plugin": "11.10.2",
|
||||
"@emotion/css": "11.10.0",
|
||||
"@emotion/jest": "11.10.0",
|
||||
"@emotion/react": "11.10.4",
|
||||
"@emotion/styled": "11.10.4",
|
||||
"@mui/icons-material": "5.10.9",
|
||||
"@mui/material": "5.10.9",
|
||||
"@mui/styles": "5.10.9",
|
||||
"@rematch/core": "2.2.0",
|
||||
"@rematch/loading": "2.1.2",
|
||||
"@rematch/persist": "2.1.2",
|
||||
"@testing-library/dom": "8.19.0",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "12.1.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.8",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-virtualized": "9.21.21",
|
||||
"@types/redux": "3.6.0",
|
||||
"@verdaccio/node-api": "workspace:6.0.0-6-next.50",
|
||||
"@verdaccio/types": "workspace:*",
|
||||
"babel-loader": "8.2.5",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"country-flag-icons": "1.5.5",
|
||||
"css-loader": "6.7.1",
|
||||
"dayjs": "1.11.5",
|
||||
"friendly-errors-webpack-plugin": "1.7.0",
|
||||
|
@ -46,33 +46,33 @@
|
|||
"html-webpack-plugin": "5.5.0",
|
||||
"i18next": "20.6.1",
|
||||
"in-publish": "2.0.1",
|
||||
"country-flag-icons": "1.5.5",
|
||||
"js-base64": "3.7.2",
|
||||
"js-yaml": "3.14.1",
|
||||
"localstorage-memory": "1.0.3",
|
||||
"lodash": "4.17.21",
|
||||
"mini-css-extract-plugin": "2.6.1",
|
||||
"msw": "0.47.4",
|
||||
"mutationobserver-shim": "0.3.7",
|
||||
"node-mocks-http": "1.11.0",
|
||||
"normalize.css": "8.0.1",
|
||||
"react-markdown": "8.0.3",
|
||||
"react-json-view": "1.21.3",
|
||||
"remark-gfm": "3.0.1",
|
||||
"optimize-css-assets-webpack-plugin": "6.0.1",
|
||||
"ora": "5.4.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-hook-form": "7.37.0",
|
||||
"raw-loader": "4.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.39.2",
|
||||
"react-hot-loader": "4.13.0",
|
||||
"react-i18next": "11.18.6",
|
||||
"react-i18next": "12.0.0",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-markdown": "8.0.3",
|
||||
"react-redux": "7.2.9",
|
||||
"react-router": "5.3.4",
|
||||
"react-router-dom": "5.3.4",
|
||||
"react-virtualized": "9.22.3",
|
||||
"react-redux": "7.2.9",
|
||||
"redux": "4.2.0",
|
||||
"redux-persist": "6.0.0",
|
||||
"remark-gfm": "3.0.1",
|
||||
"rimraf": "3.0.2",
|
||||
"raw-loader": "4.0.2",
|
||||
"msw": "0.47.4",
|
||||
"style-loader": "3.3.1",
|
||||
"stylelint": "14.14.0",
|
||||
"stylelint-config-recommended": "7.0.0",
|
||||
|
@ -86,7 +86,7 @@
|
|||
"webpack": "5.74.0",
|
||||
"webpack-bundle-analyzer": "4.6.1",
|
||||
"webpack-bundle-size-analyzer": "3.1.0",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "3.11.3",
|
||||
"webpack-manifest-plugin": "4.1.1",
|
||||
"webpack-merge": "5.8.0",
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
renderWithStore,
|
||||
screen,
|
||||
waitFor,
|
||||
} from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
import { renderWithStore, screen } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
|
||||
// eslint-disable-next-line jest/no-mocks-import
|
||||
import { generateTokenWithTimeRange } from '../../jest/unit/components/__mocks__/token';
|
||||
import { store } from '../store';
|
||||
import App from './App';
|
||||
|
||||
|
@ -41,84 +33,6 @@ jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(600);
|
|||
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
describe('<App />', () => {
|
||||
describe('login - log out', () => {
|
||||
test('handleLogout - logouts the user and clear localstorage', async () => {
|
||||
const { queryByTestId } = renderWithStore(<App />, store);
|
||||
store.dispatch.login.logInUser({
|
||||
username: 'verdaccio',
|
||||
token: generateTokenWithTimeRange(24),
|
||||
});
|
||||
|
||||
// wait for the Account's circle element component appearance and return the element
|
||||
const accountCircleElement = await waitFor(() => queryByTestId('logInDialogIcon'));
|
||||
expect(accountCircleElement).toBeTruthy();
|
||||
|
||||
if (accountCircleElement) {
|
||||
fireEvent.click(accountCircleElement);
|
||||
|
||||
// wait for the Button's logout element component appearance and return the element
|
||||
const buttonLogoutElement = await waitFor(() => queryByTestId('logOutDialogIcon'));
|
||||
expect(buttonLogoutElement).toBeTruthy();
|
||||
|
||||
if (buttonLogoutElement) {
|
||||
fireEvent.click(buttonLogoutElement);
|
||||
|
||||
expect(queryByTestId('greetings-label')).toBeFalsy();
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
||||
const { queryByTestId, queryAllByText } = renderWithStore(<App />, store);
|
||||
store.dispatch.login.logInUser({
|
||||
username: 'verdaccio',
|
||||
token: generateTokenWithTimeRange(24),
|
||||
});
|
||||
|
||||
// wait for the Account's circle element component appearance and return the element
|
||||
const accountCircleElement = await waitFor(() => queryByTestId('logInDialogIcon'));
|
||||
expect(accountCircleElement).toBeTruthy();
|
||||
|
||||
if (accountCircleElement) {
|
||||
fireEvent.click(accountCircleElement);
|
||||
|
||||
// wait for the Greeting's label element component appearance and return the element
|
||||
const greetingsLabelElement = await waitFor(() => queryByTestId('greetings-label'));
|
||||
expect(greetingsLabelElement).toBeTruthy();
|
||||
|
||||
if (greetingsLabelElement) {
|
||||
expect(queryAllByText('verdaccio')).toBeTruthy();
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('list packages', () => {
|
||||
test('should display the Header component', async () => {
|
||||
renderWithStore(<App />, store);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('loading')).toBeTruthy();
|
||||
});
|
||||
|
||||
// wait for the Header component appearance and return the element
|
||||
const headerElement = await waitFor(() => screen.queryByTestId('header'));
|
||||
expect(headerElement).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should display package lists', async () => {
|
||||
act(() => {
|
||||
renderWithStore(<App />, store);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('package-item-list')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(store.getState().packages.response).toHaveLength(1);
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('footer', () => {
|
||||
test('should display the Header component', () => {
|
||||
renderWithStore(<App />, store);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable react/jsx-max-depth */
|
||||
import styled from '@emotion/styled';
|
||||
import Box from '@mui/material/Box';
|
||||
import React, { Suspense, useEffect } from 'react';
|
||||
import React, { StrictMode, Suspense, useEffect } from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import Loading from 'verdaccio-ui/components/Loading';
|
||||
import StyleBaseline from 'verdaccio-ui/design-tokens/StyleBaseline';
|
||||
|
@ -34,20 +34,22 @@ const App: React.FC = () => {
|
|||
loadDayJSLocale();
|
||||
}, []);
|
||||
return (
|
||||
<Suspense fallback={<Loading />}>
|
||||
<StyleBaseline />
|
||||
<StyledBox display="flex" flexDirection="column" height="100%">
|
||||
<>
|
||||
<Router history={history}>
|
||||
<Header />
|
||||
<StyledBoxContent flexGrow={1}>
|
||||
<AppRoute />
|
||||
</StyledBoxContent>
|
||||
</Router>
|
||||
{configOptions.showFooter && <Footer />}
|
||||
</>
|
||||
</StyledBox>
|
||||
</Suspense>
|
||||
<StrictMode>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<StyleBaseline />
|
||||
<StyledBox display="flex" flexDirection="column" height="100%">
|
||||
<>
|
||||
<Router history={history}>
|
||||
<Header />
|
||||
<StyledBoxContent flexGrow={1}>
|
||||
<AppRoute />
|
||||
</StyledBoxContent>
|
||||
</Router>
|
||||
{configOptions.showFooter && <Footer />}
|
||||
</>
|
||||
</StyledBox>
|
||||
</Suspense>
|
||||
</StrictMode>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import {
|
||||
act,
|
||||
cleanup,
|
||||
fireEvent,
|
||||
renderWithStore,
|
||||
|
@ -15,9 +16,15 @@ import Header from './Header';
|
|||
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
describe('<Header /> component with logged in state', () => {
|
||||
afterEach(cleanup);
|
||||
beforeEach(() => {
|
||||
store.dispatch.login.logOutUser();
|
||||
});
|
||||
|
||||
test('should load the component in logged out state', () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('should load the component n logged out state', () => {
|
||||
renderWithStore(
|
||||
<Router>
|
||||
<Header />
|
||||
|
@ -53,15 +60,15 @@ describe('<Header /> component with logged in state', () => {
|
|||
store
|
||||
);
|
||||
|
||||
store.dispatch.login.logOutUser();
|
||||
|
||||
const loginBtn = screen.getByTestId('header--button-login');
|
||||
fireEvent.click(loginBtn);
|
||||
|
||||
const loginDialog = await waitFor(() => screen.getByTestId('login--dialog'));
|
||||
|
||||
expect(loginDialog).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should logout the user', async () => {
|
||||
test('should login and logout the user', async () => {
|
||||
const { getByText, getByTestId } = renderWithStore(
|
||||
<Router>
|
||||
<Header />
|
||||
|
@ -69,14 +76,27 @@ describe('<Header /> component with logged in state', () => {
|
|||
store
|
||||
);
|
||||
|
||||
store.dispatch.login.logInUser({ username: 'store', token: '12345' });
|
||||
|
||||
fireEvent.click(screen.getByText('Login'));
|
||||
const userNameInput = screen.getByPlaceholderText('Your username');
|
||||
fireEvent.focus(userNameInput);
|
||||
fireEvent.change(userNameInput, { target: { value: 'xyz' } });
|
||||
const passwordInput = screen.getByPlaceholderText('Your strong password');
|
||||
fireEvent.focus(passwordInput);
|
||||
await act(async () => {
|
||||
fireEvent.change(passwordInput, { target: { value: '1234' } });
|
||||
});
|
||||
const signInButton = screen.getByTestId('login-dialog-form-login-button');
|
||||
await act(async () => {
|
||||
fireEvent.click(signInButton);
|
||||
});
|
||||
await waitFor(() => getByTestId('logInDialogIcon'));
|
||||
const headerMenuAccountCircle = getByTestId('logInDialogIcon');
|
||||
fireEvent.click(headerMenuAccountCircle);
|
||||
|
||||
// wait for button Logout's appearance and return the element
|
||||
// // wait for button Logout's appearance and return the element
|
||||
const logoutBtn = await waitFor(() => getByText('Logout'));
|
||||
fireEvent.click(logoutBtn);
|
||||
await waitFor(() => getByText('Login'));
|
||||
expect(getByText('Login')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const Header: React.FC = () => {
|
|||
const dispatch = useDispatch<Dispatch>();
|
||||
const handleLogout = () => {
|
||||
dispatch.login.logOutUser();
|
||||
setShowLoginModal(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -69,6 +69,7 @@ const LoginDialogForm = memo(({ onSubmit, error }: Props) => {
|
|||
required: { value: true, message: t('form-validation.required-field') },
|
||||
minLength: { value: 2, message: t('form-validation.required-min-length', { length: 2 }) },
|
||||
})}
|
||||
data-testid="password"
|
||||
label={t('form.password')}
|
||||
margin="normal"
|
||||
name="password"
|
||||
|
|
|
@ -3,6 +3,7 @@ import BugReportIcon from '@mui/icons-material/BugReport';
|
|||
import DownloadIcon from '@mui/icons-material/CloudDownload';
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import RawOnIcon from '@mui/icons-material/RawOn';
|
||||
import FabMUI from '@mui/material/Fab';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -10,10 +11,9 @@ import { useDispatch } from 'react-redux';
|
|||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
|
||||
import { Dispatch } from '../../store/store';
|
||||
import FloatingActionButton from '../FloatingActionButton';
|
||||
import Link from '../Link';
|
||||
|
||||
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(({ theme }) => ({
|
||||
export const Fab = styled(FabMUI)<{ theme?: Theme }>(({ theme }) => ({
|
||||
backgroundColor:
|
||||
theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue,
|
||||
color: theme?.palette.white,
|
||||
|
|
|
@ -168,7 +168,6 @@ exports[`<ActionBar /> component should render the component in default state 1`
|
|||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default emotion-2 emotion-3"
|
||||
data-testid="fab"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
|
@ -202,7 +201,6 @@ exports[`<ActionBar /> component should render the component in default state 1`
|
|||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default emotion-2 emotion-3"
|
||||
data-testid="fab"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
|
@ -227,7 +225,7 @@ exports[`<ActionBar /> component should render the component in default state 1`
|
|||
aria-label="Download tarball"
|
||||
class="MuiButtonBase-root MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default MuiFab-root MuiFab-circular MuiFab-sizeSmall MuiFab-default emotion-2 emotion-3"
|
||||
data-mui-internal-clone-element="true"
|
||||
data-testid="fab"
|
||||
data-testid="download-tarball-btn"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import Fab from '@mui/material/Fab';
|
||||
import React from 'react';
|
||||
|
||||
const FloatingActionButton = (props) => {
|
||||
return <Fab {...props} data-testid="fab" />;
|
||||
};
|
||||
|
||||
export default FloatingActionButton;
|
|
@ -212,7 +212,7 @@ label[data-shrink=false]+.MuiInputBase-formControl .emotion-2:focus::-ms-input-p
|
|||
<input
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input MuiOutlinedInput-input emotion-2"
|
||||
id="mui-1"
|
||||
id=":r0:"
|
||||
name="test"
|
||||
type="text"
|
||||
value="test"
|
||||
|
|
|
@ -22,7 +22,7 @@ function getDarkModeDefault(darkModeConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
const ThemeProviderWrapper: React.FC = ({ children }) => {
|
||||
const ThemeProviderWrapper: React.FC<{ children: any }> = ({ children }) => {
|
||||
const currentLanguage = i18next.languages?.[0];
|
||||
const { configOptions } = useConfig();
|
||||
const isDarkModeDefault = getDarkModeDefault(configOptions.darkMode);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { Provider } from 'react-redux';
|
||||
import AppConfigurationContext from 'verdaccio-ui/providers/config';
|
||||
|
||||
|
@ -9,29 +9,23 @@ import StyleBaseline from './design-tokens/StyleBaseline';
|
|||
import ThemeProvider from './design-tokens/ThemeProvider';
|
||||
import { store } from './store';
|
||||
|
||||
const rootNode = document.getElementById('root');
|
||||
const renderApp = (Component: React.ElementType): void => {
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<AppContainer>
|
||||
<AppConfigurationContext>
|
||||
<ThemeProvider>
|
||||
<StyleBaseline />
|
||||
<Component />
|
||||
</ThemeProvider>
|
||||
</AppConfigurationContext>
|
||||
</AppContainer>
|
||||
</Provider>,
|
||||
rootNode
|
||||
);
|
||||
};
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container as HTMLElement);
|
||||
|
||||
renderApp(App);
|
||||
const AppContainer = () => (
|
||||
<Provider store={store}>
|
||||
<AppConfigurationContext>
|
||||
<ThemeProvider>
|
||||
<StyleBaseline />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</AppConfigurationContext>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
root.render(<AppContainer />);
|
||||
|
||||
// @ts-expect-error
|
||||
if (module.hot) {
|
||||
// @ts-expect-error
|
||||
module.hot.accept('./App', () => {
|
||||
renderApp(App);
|
||||
});
|
||||
hot(AppContainer);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { renderWithStore, screen } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
import { renderWithStore, screen, waitFor } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
|
||||
import { store } from '../../../store';
|
||||
import { DetailContext } from '../context';
|
||||
|
@ -15,6 +15,13 @@ const ComponentToBeRendered: React.FC<{ contextValue: DetailContextProps }> = ({
|
|||
</DetailContext.Provider>
|
||||
);
|
||||
|
||||
// https://stackoverflow.com/a/54010619/308341
|
||||
jest.mock('react', () => {
|
||||
const React = jest.requireActual('react');
|
||||
React.Suspense = ({ children }) => children;
|
||||
return React;
|
||||
});
|
||||
|
||||
const detailContextValue: DetailContextProps = {
|
||||
packageName: 'foo',
|
||||
readMe: 'test',
|
||||
|
@ -40,8 +47,8 @@ const detailContextValue: DetailContextProps = {
|
|||
};
|
||||
|
||||
describe('DetailSidebar', () => {
|
||||
test('should render commonjs module icon', () => {
|
||||
renderWithStore(
|
||||
test('should render commonjs module icon', async () => {
|
||||
const { getByAltText } = renderWithStore(
|
||||
<ComponentToBeRendered
|
||||
contextValue={_.merge(detailContextValue, {
|
||||
packageMeta: {
|
||||
|
@ -53,7 +60,8 @@ describe('DetailSidebar', () => {
|
|||
/>,
|
||||
store
|
||||
);
|
||||
expect(screen.getByAltText('commonjs')).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => expect(getByAltText('commonjs')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
test('should render ts module icon', () => {
|
||||
|
|
|
@ -107,7 +107,7 @@ describe('test Developers', () => {
|
|||
// item2.simulate('click');
|
||||
|
||||
expect(wrapper.getByText('Contributors')).toBeInTheDocument();
|
||||
fireEvent.click(wrapper.getByTestId('fab'));
|
||||
fireEvent.click(wrapper.getByRole('button'));
|
||||
|
||||
expect(wrapper.getByLabelText(packageMeta.latest.contributors[0].name)).toBeInTheDocument();
|
||||
expect(wrapper.getByLabelText(packageMeta.latest.contributors[1].name)).toBeInTheDocument();
|
||||
|
|
|
@ -2,10 +2,10 @@ import styled from '@emotion/styled';
|
|||
import Add from '@mui/icons-material/Add';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Box from '@mui/material/Box';
|
||||
import FabMUI from '@mui/material/Fab';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import FloatingActionButton from 'verdaccio-ui/components/FloatingActionButton';
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
|
||||
import { DetailContext } from '../..';
|
||||
|
@ -17,7 +17,7 @@ export enum DeveloperType {
|
|||
MAINTAINERS = 'maintainers',
|
||||
}
|
||||
|
||||
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>((props) => ({
|
||||
export const Fab = styled(FabMUI)<{ theme?: Theme }>((props) => ({
|
||||
backgroundColor: props.theme?.palette.primary.main,
|
||||
color: props.theme?.palette.white,
|
||||
}));
|
||||
|
@ -70,11 +70,13 @@ const Developers: React.FC<Props> = ({ type, visibleMax = VISIBLE_MAX }) => {
|
|||
<>
|
||||
<DevelopersTitle type={type} />
|
||||
<StyledBox display="flex" flexWrap="wrap" margin="10px 0 10px 0">
|
||||
{visibleDevelopers.map((visibleDeveloper) => (
|
||||
<Tooltip key={visibleDeveloper.email} title={visibleDeveloper.name}>
|
||||
<Avatar alt={visibleDeveloper.name} src={visibleDeveloper.avatar} />
|
||||
</Tooltip>
|
||||
))}
|
||||
{visibleDevelopers.map((visibleDeveloper) => {
|
||||
return (
|
||||
<Tooltip key={visibleDeveloper.email} title={visibleDeveloper.name}>
|
||||
<Avatar alt={visibleDeveloper.name} src={visibleDeveloper.avatar} />
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
{visibleDevelopersMax < developers.length && (
|
||||
<Fab onClick={handleSetVisibleDevelopersMax} size="small">
|
||||
<Add />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import styled from '@emotion/styled';
|
||||
import FloatingActionButton from 'verdaccio-ui/components/FloatingActionButton';
|
||||
import FabMUI from '@mui/material/Fab';
|
||||
import Text from 'verdaccio-ui/components/Text';
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
|
||||
|
@ -24,7 +24,7 @@ export const StyledText = styled(Text)<{ theme?: Theme }>((props) => ({
|
|||
textTransform: 'capitalize',
|
||||
}));
|
||||
|
||||
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>((props) => ({
|
||||
export const Fab = styled(FabMUI)<{ theme?: Theme }>((props) => ({
|
||||
backgroundColor: props.theme?.palette.primary.main,
|
||||
color: props.theme?.palette.white,
|
||||
}));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import styled from '@emotion/styled';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import FabMUI from '@mui/material/Fab';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import FloatingActionButton from 'verdaccio-ui/components/FloatingActionButton';
|
||||
import Text from 'verdaccio-ui/components/Text';
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
|
||||
|
@ -22,7 +22,7 @@ export const DistChips = styled(Chip)({
|
|||
marginTop: 5,
|
||||
});
|
||||
|
||||
export const DownloadButton = styled(FloatingActionButton)<{ theme?: Theme }>((props) => ({
|
||||
export const DownloadButton = styled(FabMUI)<{ theme?: Theme }>((props) => ({
|
||||
backgroundColor: props.theme && props.theme.palette.primary.main,
|
||||
color: props.theme && props.theme.palette.white,
|
||||
}));
|
||||
|
|
|
@ -6,7 +6,6 @@ import List from '@mui/material/List';
|
|||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Label from 'verdaccio-ui/components/Label';
|
||||
import { default as MuiText } from 'verdaccio-ui/components/Text';
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
|
||||
export const OverviewItem = styled('span')<{ theme?: Theme }>(({ theme }) => ({
|
||||
|
@ -104,7 +103,7 @@ export const PackageListItemText = styled(ListItemText)({
|
|||
paddingRight: 0,
|
||||
});
|
||||
|
||||
export const Description = styled(MuiText)<{ theme?: Theme }>(({ theme }) => ({
|
||||
export const Description = styled('span')<{ theme?: Theme }>(({ theme }) => ({
|
||||
color: theme?.palette.mode === 'light' ? theme?.palette.greyDark2 : theme?.palette.white,
|
||||
fontSize: '14px',
|
||||
paddingRight: 0,
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { createModel } from '@rematch/core';
|
||||
|
||||
import { Package } from '@verdaccio/types';
|
||||
import { Manifest } from '@verdaccio/types';
|
||||
|
||||
import type { RootModel } from '.';
|
||||
import API from '../../providers/API/api';
|
||||
|
||||
export const packages = createModel<RootModel>()({
|
||||
state: {
|
||||
response: [] as Package[],
|
||||
response: [] as Manifest[],
|
||||
},
|
||||
reducers: {
|
||||
savePackages(state, response: Package[]) {
|
||||
savePackages(state, response: Manifest[]) {
|
||||
return {
|
||||
...state,
|
||||
response,
|
||||
|
@ -21,7 +21,10 @@ export const packages = createModel<RootModel>()({
|
|||
async getPackages(_payload, state) {
|
||||
const basePath = state.configuration.config.base;
|
||||
try {
|
||||
const payload: Package[] = await API.request(`${basePath}-/verdaccio/data/packages`, 'GET');
|
||||
const payload: Manifest[] = await API.request(
|
||||
`${basePath}-/verdaccio/data/packages`,
|
||||
'GET'
|
||||
);
|
||||
dispatch.packages.savePackages(payload);
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
661
pnpm-lock.yaml
661
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue