mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
refactor: enable e2e for UI (#2036)
* refactor: enable e2e for UI * refactor: enable e2e for UI * refactor: enable e2e for UI * refactor: enable e2e for UI * refactor: enable e2e for UI * refactor: enable e2e for UI
This commit is contained in:
parent
e9e4552658
commit
821bd776f3
28 changed files with 1103 additions and 29 deletions
30
.github/workflows/ci-e2e-ui.yml
vendored
30
.github/workflows/ci-e2e-ui.yml
vendored
|
@ -1,6 +1,18 @@
|
||||||
name: E2E UI
|
name: E2E UI
|
||||||
|
|
||||||
on: [pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 5.x
|
||||||
|
- 'changeset-release/5.x'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- 'packages/**'
|
||||||
|
- 'jest/**'
|
||||||
|
- 'package.json'
|
||||||
|
- 'pnpm-workspace.yaml'
|
||||||
|
- 'test/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
|
@ -16,12 +28,18 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.1
|
- uses: actions/checkout@v2.3.1
|
||||||
- name: Use Node ${{ matrix.node_version }}
|
- name: Use Node ${{ matrix.node_version }}
|
||||||
uses: actions/setup-node@v2.1.5
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node_version }}
|
node_version: ${{ matrix.node_version }}
|
||||||
|
- name: Install pnpm
|
||||||
|
run: npm i -g pnpm@latest
|
||||||
- name: Install
|
- name: Install
|
||||||
run: yarn install --immutable
|
run: pnpm recursive install
|
||||||
|
- name: Clean
|
||||||
|
run: pnpm clean
|
||||||
- name: Build
|
- name: Build
|
||||||
run: yarn code:build
|
run: pnpm build
|
||||||
- name: Test UI
|
- name: Test UI
|
||||||
run: yarn run test:e2e
|
run: pnpm test:e2e:ui
|
||||||
|
env:
|
||||||
|
DEBUG: verdaccio:e2e*
|
||||||
|
|
6
.github/workflows/ci-e2e.yml
vendored
6
.github/workflows/ci-e2e.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: CI E2E
|
name: E2E CLI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -7,12 +7,12 @@ on:
|
||||||
- 'changeset-release/5.x'
|
- 'changeset-release/5.x'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- .changeset/**
|
|
||||||
- .github/workflows/ci.yml
|
- .github/workflows/ci.yml
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
- 'jest/**'
|
- 'jest/**'
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'pnpm-workspace.yaml'
|
- 'pnpm-workspace.yaml'
|
||||||
|
- 'test/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
|
@ -39,7 +39,7 @@ jobs:
|
||||||
run: pnpm clean
|
run: pnpm clean
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
- name: Test
|
- name: Test CLI
|
||||||
run: pnpm test:e2e:cli
|
run: pnpm test:e2e:cli
|
||||||
env:
|
env:
|
||||||
DEBUG: verdaccio:e2e*
|
DEBUG: verdaccio:e2e*
|
||||||
|
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
|
@ -5,7 +5,11 @@ on:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 5.x
|
- 5.x
|
||||||
- 'changeset-release/5.x'
|
- 'changeset-release/5.x'
|
||||||
|
- 'dev/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- 'packages/**'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 2 * * 4'
|
- cron: '0 2 * * 4'
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
CONTRIBUTORS.md
|
CONTRIBUTORS.md
|
||||||
node_modules/
|
node_modules/
|
||||||
|
**/coverage/**
|
||||||
**/static/*.js
|
**/static/*.js
|
||||||
**/build/*.js
|
**/build/*.js
|
||||||
packages/core/local-storage/_storage/**
|
packages/core/local-storage/_storage/**
|
||||||
|
|
|
@ -128,6 +128,7 @@
|
||||||
"lint": "eslint \"**/*.{js,jsx,ts,tsx}\"",
|
"lint": "eslint \"**/*.{js,jsx,ts,tsx}\"",
|
||||||
"test": "pnpm recursive test --filter ./packages",
|
"test": "pnpm recursive test --filter ./packages",
|
||||||
"test:e2e:cli": "pnpm test --filter ...@verdaccio/e2e-cli",
|
"test:e2e:cli": "pnpm test --filter ...@verdaccio/e2e-cli",
|
||||||
|
"test:e2e:ui": "pnpm test --filter ...@verdaccio/e2e-ui",
|
||||||
"start": "concurrently --kill-others \"pnpm start:server\" \"pnpm start:web\"",
|
"start": "concurrently --kill-others \"pnpm start:server\" \"pnpm start:web\"",
|
||||||
"start:server": "node packages/verdaccio/debug/bootstrap.js --listen 8000",
|
"start:server": "node packages/verdaccio/debug/bootstrap.js --listen 8000",
|
||||||
"start:web": "pnpm start --filter ...@verdaccio/ui-theme",
|
"start:web": "pnpm start --filter ...@verdaccio/ui-theme",
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
"@testing-library/dom": "^7.29.0",
|
"@testing-library/dom": "^7.29.0",
|
||||||
"@testing-library/jest-dom": "^5.11.6",
|
"@testing-library/jest-dom": "^5.11.6",
|
||||||
"@testing-library/react": "10.4.9",
|
"@testing-library/react": "10.4.9",
|
||||||
"@verdaccio/commons-api": "workspace:10.0.0-alpha.1",
|
|
||||||
"@verdaccio/node-api": "workspace:5.0.0-alpha.1",
|
"@verdaccio/node-api": "workspace:5.0.0-alpha.1",
|
||||||
"autosuggest-highlight": "3.1.1",
|
"autosuggest-highlight": "3.1.1",
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
|
@ -56,11 +55,7 @@
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||||
"ora": "4.0.4",
|
"ora": "4.0.4",
|
||||||
"prop-types": "15.7.2",
|
|
||||||
"puppeteer": "5.3.1",
|
|
||||||
"react": "16.13.1",
|
|
||||||
"react-autosuggest": "10.0.2",
|
"react-autosuggest": "10.0.2",
|
||||||
"react-dom": "16.13.1",
|
|
||||||
"react-hook-form": "3.29.4",
|
"react-hook-form": "3.29.4",
|
||||||
"react-hot-loader": "4.12.21",
|
"react-hot-loader": "4.12.21",
|
||||||
"react-i18next": "^11.8.3",
|
"react-i18next": "^11.8.3",
|
||||||
|
@ -70,7 +65,10 @@
|
||||||
"request": "2.88.2",
|
"request": "2.88.2",
|
||||||
"resolve-url-loader": "3.1.1",
|
"resolve-url-loader": "3.1.1",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
|
"react": "16.13.1",
|
||||||
|
"react-dom": "16.13.1",
|
||||||
"source-map-loader": "1.1.0",
|
"source-map-loader": "1.1.0",
|
||||||
|
"prop-types": "15.7.2",
|
||||||
"standard-version": "9.0.0",
|
"standard-version": "9.0.0",
|
||||||
"style-loader": "1.2.1",
|
"style-loader": "1.2.1",
|
||||||
"stylelint": "13.7.2",
|
"stylelint": "13.7.2",
|
||||||
|
@ -124,7 +122,6 @@
|
||||||
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
||||||
"start": "babel-node tools/dev.server.js",
|
"start": "babel-node tools/dev.server.js",
|
||||||
"test:clean": "jest --clearCache",
|
"test:clean": "jest --clearCache",
|
||||||
"test:e2e": " jest --config ./test/jest.config.e2e.js",
|
|
||||||
"test": "cross-env BABEL_ENV=test cross-env NODE_ENV=test cross-env TZ=UTC jest --config ./jest/jest.config.js --maxWorkers 2 --passWithNoTests",
|
"test": "cross-env BABEL_ENV=test cross-env NODE_ENV=test cross-env TZ=UTC jest --config ./jest/jest.config.js --maxWorkers 2 --passWithNoTests",
|
||||||
"test:update-snapshot": "yarn run test -- -u",
|
"test:update-snapshot": "yarn run test -- -u",
|
||||||
"test:size": "bundlesize",
|
"test:size": "bundlesize",
|
||||||
|
|
|
@ -640,7 +640,6 @@ importers:
|
||||||
'@testing-library/dom': 7.29.0
|
'@testing-library/dom': 7.29.0
|
||||||
'@testing-library/jest-dom': 5.11.6
|
'@testing-library/jest-dom': 5.11.6
|
||||||
'@testing-library/react': 10.4.9_react-dom@16.13.1+react@16.13.1
|
'@testing-library/react': 10.4.9_react-dom@16.13.1+react@16.13.1
|
||||||
'@verdaccio/commons-api': 'link:../../core/commons-api'
|
|
||||||
'@verdaccio/node-api': 'link:../../node-api'
|
'@verdaccio/node-api': 'link:../../node-api'
|
||||||
autosuggest-highlight: 3.1.1
|
autosuggest-highlight: 3.1.1
|
||||||
babel-loader: 8.2.2_webpack@5.10.1
|
babel-loader: 8.2.2_webpack@5.10.1
|
||||||
|
@ -672,7 +671,6 @@ importers:
|
||||||
optimize-css-assets-webpack-plugin: 5.0.4_webpack@5.10.1
|
optimize-css-assets-webpack-plugin: 5.0.4_webpack@5.10.1
|
||||||
ora: 4.0.4
|
ora: 4.0.4
|
||||||
prop-types: 15.7.2
|
prop-types: 15.7.2
|
||||||
puppeteer: 5.3.1
|
|
||||||
react: 16.13.1
|
react: 16.13.1
|
||||||
react-autosuggest: 10.0.2_react@16.13.1
|
react-autosuggest: 10.0.2_react@16.13.1
|
||||||
react-dom: 16.13.1_react@16.13.1
|
react-dom: 16.13.1_react@16.13.1
|
||||||
|
@ -720,7 +718,6 @@ importers:
|
||||||
'@testing-library/dom': ^7.29.0
|
'@testing-library/dom': ^7.29.0
|
||||||
'@testing-library/jest-dom': ^5.11.6
|
'@testing-library/jest-dom': ^5.11.6
|
||||||
'@testing-library/react': 10.4.9
|
'@testing-library/react': 10.4.9
|
||||||
'@verdaccio/commons-api': 'workspace:10.0.0-alpha.1'
|
|
||||||
'@verdaccio/node-api': 'workspace:5.0.0-alpha.1'
|
'@verdaccio/node-api': 'workspace:5.0.0-alpha.1'
|
||||||
autosuggest-highlight: 3.1.1
|
autosuggest-highlight: 3.1.1
|
||||||
babel-loader: ^8.2.2
|
babel-loader: ^8.2.2
|
||||||
|
@ -752,7 +749,6 @@ importers:
|
||||||
optimize-css-assets-webpack-plugin: ^5.0.4
|
optimize-css-assets-webpack-plugin: ^5.0.4
|
||||||
ora: 4.0.4
|
ora: 4.0.4
|
||||||
prop-types: 15.7.2
|
prop-types: 15.7.2
|
||||||
puppeteer: 5.3.1
|
|
||||||
react: 16.13.1
|
react: 16.13.1
|
||||||
react-autosuggest: 10.0.2
|
react-autosuggest: 10.0.2
|
||||||
react-dom: 16.13.1
|
react-dom: 16.13.1
|
||||||
|
@ -982,6 +978,27 @@ importers:
|
||||||
request: ^2.88.2
|
request: ^2.88.2
|
||||||
semver: ^7.3.4
|
semver: ^7.3.4
|
||||||
yarn: 1.22.10
|
yarn: 1.22.10
|
||||||
|
test/e2e-ui:
|
||||||
|
devDependencies:
|
||||||
|
'@verdaccio/commons-api': 'link:../../packages/core/commons-api'
|
||||||
|
'@verdaccio/ui-theme': 'link:../../packages/plugins/ui-theme'
|
||||||
|
debug: 4.3.1
|
||||||
|
kleur: 4.1.3
|
||||||
|
lodash: 4.17.20
|
||||||
|
mkdirp: 1.0.4
|
||||||
|
puppeteer: 5.5.0
|
||||||
|
request: 2.88.2
|
||||||
|
rimraf: 3.0.2
|
||||||
|
specifiers:
|
||||||
|
'@verdaccio/commons-api': 'workspace:10.0.0-alpha.1'
|
||||||
|
'@verdaccio/ui-theme': 'workspace:5.0.0-alpha.1'
|
||||||
|
debug: 4.3.1
|
||||||
|
kleur: ^4.1.3
|
||||||
|
lodash: ^4.17.20
|
||||||
|
mkdirp: ^1.0.4
|
||||||
|
puppeteer: ^5.5.0
|
||||||
|
request: ^2.88.2
|
||||||
|
rimraf: ^3.0.2
|
||||||
website:
|
website:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emotion/core': 10.0.28_react@16.13.1
|
'@emotion/core': 10.0.28_react@16.13.1
|
||||||
|
@ -11291,10 +11308,10 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-7/nIzKdQ8y2K0imjIP7dyg2GJ2h38Ps6VOMXWZHIarNDV3p6mTXyEugKFnkmsZ2DD58JEG34ILyVb3qdOMmP9w==
|
integrity: sha512-7/nIzKdQ8y2K0imjIP7dyg2GJ2h38Ps6VOMXWZHIarNDV3p6mTXyEugKFnkmsZ2DD58JEG34ILyVb3qdOMmP9w==
|
||||||
/devtools-protocol/0.0.799653:
|
/devtools-protocol/0.0.818844:
|
||||||
dev: true
|
dev: true
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==
|
integrity: sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==
|
||||||
/diacritic/0.0.2:
|
/diacritic/0.0.2:
|
||||||
dev: true
|
dev: true
|
||||||
resolution:
|
resolution:
|
||||||
|
@ -17586,6 +17603,12 @@ packages:
|
||||||
node: '>=6'
|
node: '>=6'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-FGCCxczbrZuF5CtMeO0xfnjhzkVZSXfcWK90IPLucDWZwskrpYN7pmRIgvd8muU0mrPrzy4A2RBGuwCjLHI+nw==
|
integrity: sha512-FGCCxczbrZuF5CtMeO0xfnjhzkVZSXfcWK90IPLucDWZwskrpYN7pmRIgvd8muU0mrPrzy4A2RBGuwCjLHI+nw==
|
||||||
|
/kleur/4.1.3:
|
||||||
|
dev: true
|
||||||
|
engines:
|
||||||
|
node: '>=6'
|
||||||
|
resolution:
|
||||||
|
integrity: sha512-H1tr8QP2PxFTNwAFM74Mui2b6ovcY9FoxJefgrwxY+OCJcq01k5nvhf4M/KnizzrJvLRap5STUy7dgDV35iUBw==
|
||||||
/known-css-properties/0.19.0:
|
/known-css-properties/0.19.0:
|
||||||
dev: true
|
dev: true
|
||||||
resolution:
|
resolution:
|
||||||
|
@ -21506,12 +21529,13 @@ packages:
|
||||||
node: '>=8'
|
node: '>=8'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==
|
integrity: sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==
|
||||||
/puppeteer/5.3.1:
|
/puppeteer/5.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.1
|
debug: 4.3.1
|
||||||
devtools-protocol: 0.0.799653
|
devtools-protocol: 0.0.818844
|
||||||
extract-zip: 2.0.1
|
extract-zip: 2.0.1
|
||||||
https-proxy-agent: 4.0.0
|
https-proxy-agent: 4.0.0
|
||||||
|
node-fetch: 2.6.1
|
||||||
pkg-dir: 4.2.0
|
pkg-dir: 4.2.0
|
||||||
progress: 2.0.3
|
progress: 2.0.3
|
||||||
proxy-from-env: 1.1.0
|
proxy-from-env: 1.1.0
|
||||||
|
@ -21524,7 +21548,7 @@ packages:
|
||||||
node: '>=10.18.1'
|
node: '>=10.18.1'
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==
|
integrity: sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==
|
||||||
/q/1.5.1:
|
/q/1.5.1:
|
||||||
engines:
|
engines:
|
||||||
node: '>=0.6.0'
|
node: '>=0.6.0'
|
||||||
|
|
|
@ -3,4 +3,4 @@ packages:
|
||||||
- packages/core/*
|
- packages/core/*
|
||||||
- packages/plugins/*
|
- packages/plugins/*
|
||||||
- website
|
- website
|
||||||
- test/e2e-cli
|
- test/e2e-*
|
||||||
|
|
6
test/README.md
Normal file
6
test/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# E2E Testing
|
||||||
|
|
||||||
|
This folder is composed of two strategies:
|
||||||
|
|
||||||
|
- E2E for CLI
|
||||||
|
- E2E for the UI (puppeteer)
|
|
@ -1,4 +1,4 @@
|
||||||
# E2E Testing
|
# E2E CLI Testing
|
||||||
|
|
||||||
## What is included on these test?
|
## What is included on these test?
|
||||||
|
|
||||||
|
|
3
test/e2e-ui/.babelrc
Normal file
3
test/e2e-ui/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../.babelrc"
|
||||||
|
}
|
15
test/e2e-ui/.eslintrc
Normal file
15
test/e2e-ui/.eslintrc
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"no-console": 0,
|
||||||
|
"new-cap": 0,
|
||||||
|
"max-len": 0
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"VERDACCIO_API_URL": true,
|
||||||
|
"__DEBUG__": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"jest/globals": true
|
||||||
|
}
|
||||||
|
}
|
9
test/e2e-ui/README.md
Normal file
9
test/e2e-ui/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# E2E UI Testing
|
||||||
|
|
||||||
|
## What is included on these test?
|
||||||
|
|
||||||
|
- Run acceptance test on UI
|
||||||
|
- Check home page works correctly
|
||||||
|
- Check navigation
|
||||||
|
- Check sidebar
|
||||||
|
- Check protected packages works
|
27
test/e2e-ui/config/config-protected-e2e.yaml
Normal file
27
test/e2e-ui/config/config-protected-e2e.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
web:
|
||||||
|
enable: true
|
||||||
|
title: verdaccio-server-protected-e2e
|
||||||
|
|
||||||
|
store:
|
||||||
|
memory:
|
||||||
|
limit: 10
|
||||||
|
|
||||||
|
auth:
|
||||||
|
auth-memory:
|
||||||
|
users:
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
password: test
|
||||||
|
|
||||||
|
logs:
|
||||||
|
- { type: stdout, format: pretty, level: info }
|
||||||
|
|
||||||
|
packages:
|
||||||
|
'protected-*':
|
||||||
|
access: $authenticated
|
||||||
|
publish: $authenticated
|
||||||
|
|
||||||
|
listen: 0.0.0.0:55552
|
||||||
|
|
||||||
|
# expose internal methods
|
||||||
|
_debug: true
|
30
test/e2e-ui/config/config-scoped-e2e.yaml
Normal file
30
test/e2e-ui/config/config-scoped-e2e.yaml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
web:
|
||||||
|
enable: true
|
||||||
|
title: verdaccio-server-e2e
|
||||||
|
|
||||||
|
store:
|
||||||
|
memory:
|
||||||
|
limit: 10
|
||||||
|
|
||||||
|
auth:
|
||||||
|
auth-memory:
|
||||||
|
users:
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
password: test
|
||||||
|
|
||||||
|
logs:
|
||||||
|
- { type: stdout, format: pretty, level: info }
|
||||||
|
|
||||||
|
packages:
|
||||||
|
'@*/*':
|
||||||
|
access: $all
|
||||||
|
publish: $all
|
||||||
|
'**':
|
||||||
|
access: $all
|
||||||
|
publish: $authenticated
|
||||||
|
|
||||||
|
listen: 0.0.0.0:55558
|
||||||
|
|
||||||
|
# expose internal methods
|
||||||
|
_debug: true
|
213
test/e2e-ui/e2e.spec.js
Normal file
213
test/e2e-ui/e2e.spec.js
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
const protectedPackageMetadata = require('./partials/pkg-protected');
|
||||||
|
const scopedPackageMetadata = require('./partials/pkg-scoped');
|
||||||
|
|
||||||
|
describe('/ (Verdaccio Page)', () => {
|
||||||
|
let page;
|
||||||
|
// this might be increased based on the delays included in all test
|
||||||
|
jest.setTimeout(20000);
|
||||||
|
|
||||||
|
const clickElement = async function (selector, options = { delay: 100 }) {
|
||||||
|
const button = await page.$(selector);
|
||||||
|
await button.focus();
|
||||||
|
await button.click(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const evaluateSignIn = async function (matchText = 'Login') {
|
||||||
|
const text = await page.evaluate(() => {
|
||||||
|
return document.querySelector('button[data-testid="header--button-login"]').textContent;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(text).toMatch(matchText);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPackages = async function () {
|
||||||
|
return await page.$$('.package-title');
|
||||||
|
};
|
||||||
|
|
||||||
|
const logIn = async function () {
|
||||||
|
await clickElement('button[data-testid="header--button-login"]');
|
||||||
|
// we fill the sign in form
|
||||||
|
const signInDialog = await page.$('#login--dialog');
|
||||||
|
const userInput = await signInDialog.$('#login--dialog-username');
|
||||||
|
expect(userInput).not.toBeNull();
|
||||||
|
const passInput = await signInDialog.$('#login--dialog-password');
|
||||||
|
expect(passInput).not.toBeNull();
|
||||||
|
await userInput.type('test', { delay: 100 });
|
||||||
|
await passInput.type('test', { delay: 100 });
|
||||||
|
await passInput.dispose();
|
||||||
|
// click on log in
|
||||||
|
const loginButton = await page.$('#login--dialog-button-submit');
|
||||||
|
expect(loginButton).toBeDefined();
|
||||||
|
await loginButton.focus();
|
||||||
|
await loginButton.click({ delay: 100 });
|
||||||
|
await page.waitFor(500);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
page = await global.__BROWSER__.newPage();
|
||||||
|
await page.goto('http://0.0.0.0:55558');
|
||||||
|
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await page.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display title', async () => {
|
||||||
|
const text = await page.title();
|
||||||
|
await page.waitFor(1000);
|
||||||
|
|
||||||
|
expect(text).toContain('verdaccio-server-e2e');
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should match title with no packages published', async () => {
|
||||||
|
const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent);
|
||||||
|
expect(text).toMatch('No Package Published Yet.');
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should match title with first step', async () => {
|
||||||
|
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
|
||||||
|
expect(text).toContain('npm adduser --registry http://0.0.0.0:55558');
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should match title with second step', async () => {
|
||||||
|
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
|
||||||
|
expect(text).toContain('npm publish --registry http://0.0.0.0:55558');
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should match button Login to sign in', async () => {
|
||||||
|
await evaluateSignIn();
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should click on sign in button', async () => {
|
||||||
|
const signInButton = await page.$('button[data-testid="header--button-login"]');
|
||||||
|
await signInButton.click();
|
||||||
|
await page.waitFor(1000);
|
||||||
|
const signInDialog = await page.$('#login--dialog');
|
||||||
|
expect(signInDialog).not.toBeNull();
|
||||||
|
const closeButton = await page.$('button[data-testid="close-login-dialog-button"]');
|
||||||
|
await closeButton.click();
|
||||||
|
await page.waitFor(500);
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should log in an user', async () => {
|
||||||
|
// we open the dialog
|
||||||
|
await logIn();
|
||||||
|
// check whether user is logged
|
||||||
|
const buttonLogout = await page.$('#header--button-logout');
|
||||||
|
expect(buttonLogout).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should logout an user', async () => {
|
||||||
|
// we assume the user is logged already
|
||||||
|
await clickElement('#header--button-account', { delay: 500 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
await clickElement('#header--button-logout > span', { delay: 500 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
await evaluateSignIn();
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should check registry info dialog', async () => {
|
||||||
|
const registryInfoButton = await page.$('#header--button-registryInfo');
|
||||||
|
registryInfoButton.click();
|
||||||
|
await page.waitFor(500);
|
||||||
|
|
||||||
|
const registryInfoDialog = await page.$('#registryInfo--dialog-container');
|
||||||
|
expect(registryInfoDialog).not.toBeNull();
|
||||||
|
|
||||||
|
const closeButton = await page.$('#registryInfo--dialog-close');
|
||||||
|
await closeButton.click();
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should publish a package', async () => {
|
||||||
|
await global.__SERVER__.putPackage(scopedPackageMetadata.name, scopedPackageMetadata);
|
||||||
|
await page.waitFor(1000);
|
||||||
|
await page.reload();
|
||||||
|
await page.waitFor(1000);
|
||||||
|
const packagesList = await getPackages();
|
||||||
|
expect(packagesList).toHaveLength(1);
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should navigate to the package detail', async () => {
|
||||||
|
const packagesList = await getPackages();
|
||||||
|
// console.log("-->packagesList:", packagesList);
|
||||||
|
const firstPackage = packagesList[0];
|
||||||
|
await firstPackage.click({ delay: 200 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
const readmeText = await page.evaluate(
|
||||||
|
() => document.querySelector('.markdown-body').textContent
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(readmeText).toMatch('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should contains last sync information', async () => {
|
||||||
|
const versionList = await page.$$('.sidebar-info .detail-info');
|
||||||
|
expect(versionList).toHaveLength(1);
|
||||||
|
});
|
||||||
|
//
|
||||||
|
|
||||||
|
test('should display dependencies tab', async () => {
|
||||||
|
const dependenciesTab = await page.$$('#dependencies-tab');
|
||||||
|
expect(dependenciesTab).toHaveLength(1);
|
||||||
|
await dependenciesTab[0].click({ delay: 200 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
const tags = await page.$$('.dep-tag');
|
||||||
|
const tag = tags[0];
|
||||||
|
const label = await page.evaluate((el) => el.innerText, tag);
|
||||||
|
expect(label).toMatch('verdaccio@');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display version tab', async () => {
|
||||||
|
const versionsTab = await page.$$('#versions-tab');
|
||||||
|
expect(versionsTab).toHaveLength(1);
|
||||||
|
await versionsTab[0].click({ delay: 200 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
const versionItems = await page.$$('.version-item');
|
||||||
|
expect(versionItems).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display uplinks tab', async () => {
|
||||||
|
const upLinksTab = await page.$$('#uplinks-tab');
|
||||||
|
expect(upLinksTab).toHaveLength(1);
|
||||||
|
await upLinksTab[0].click({ delay: 200 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display readme tab', async () => {
|
||||||
|
const readmeTab = await page.$$('#readme-tab');
|
||||||
|
expect(readmeTab).toHaveLength(1);
|
||||||
|
await readmeTab[0].click({ delay: 200 });
|
||||||
|
await page.waitFor(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should publish a protected package', async () => {
|
||||||
|
await page.goto('http://0.0.0.0:55552');
|
||||||
|
await page.waitFor(500);
|
||||||
|
await global.__SERVER_PROTECTED__.putPackage(
|
||||||
|
protectedPackageMetadata.name,
|
||||||
|
protectedPackageMetadata
|
||||||
|
);
|
||||||
|
await page.waitFor(500);
|
||||||
|
await page.reload();
|
||||||
|
await page.waitFor(500);
|
||||||
|
const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent);
|
||||||
|
expect(text).toMatch('No Package Published Yet');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should go to 404 page', async () => {
|
||||||
|
await page.goto('http://0.0.0.0:55552/-/web/detail/@verdaccio/not-found');
|
||||||
|
await page.waitFor(500);
|
||||||
|
const text = await page.evaluate(() => document.querySelector('.not-found-text').textContent);
|
||||||
|
expect(text).toMatch("Sorry, we couldn't find it...");
|
||||||
|
});
|
||||||
|
});
|
11
test/e2e-ui/jest.config.e2e.js
Normal file
11
test/e2e-ui/jest.config.e2e.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const config = require('../../jest/config');
|
||||||
|
|
||||||
|
module.exports = Object.assign({}, config, {
|
||||||
|
name: 'verdaccio-e2e-jest',
|
||||||
|
verbose: true,
|
||||||
|
collectCoverage: false,
|
||||||
|
globalSetup: './pre-setup.js',
|
||||||
|
globalTeardown: './teardown.js',
|
||||||
|
testEnvironment: './puppeteer_environment.js',
|
||||||
|
testRegex: '(/e2e.*\\.spec)\\.js',
|
||||||
|
});
|
19
test/e2e-ui/package.json
Normal file
19
test/e2e-ui/package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "@verdaccio/e2e-ui",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@verdaccio/ui-theme": "workspace:5.0.0-alpha.1",
|
||||||
|
"@verdaccio/commons-api": "workspace:10.0.0-alpha.1",
|
||||||
|
"debug": "4.3.1",
|
||||||
|
"kleur": "^4.1.3",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"puppeteer": "^5.5.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest --config jest.config.e2e.js"
|
||||||
|
}
|
||||||
|
}
|
51
test/e2e-ui/partials/pkg-protected.js
Normal file
51
test/e2e-ui/partials/pkg-protected.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
const json = {
|
||||||
|
_id: 'protected-pkg',
|
||||||
|
name: 'protected-pkg',
|
||||||
|
description: '',
|
||||||
|
'dist-tags': {
|
||||||
|
latest: '5.0.5',
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
'5.0.5': {
|
||||||
|
name: 'protected-pkg',
|
||||||
|
version: '5.0.5',
|
||||||
|
description: '',
|
||||||
|
main: 'index.js',
|
||||||
|
scripts: {
|
||||||
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
|
},
|
||||||
|
keywords: [],
|
||||||
|
author: {
|
||||||
|
name: 'User NPM',
|
||||||
|
email: 'user@domain.com',
|
||||||
|
},
|
||||||
|
license: 'ISC',
|
||||||
|
dependencies: {
|
||||||
|
verdaccio: '^2.7.2',
|
||||||
|
},
|
||||||
|
readme: '# test',
|
||||||
|
readmeFilename: 'README.md',
|
||||||
|
_id: 'protected-pkg@5.0.5',
|
||||||
|
_npmVersion: '5.5.1',
|
||||||
|
_nodeVersion: '8.7.0',
|
||||||
|
_npmUser: {},
|
||||||
|
dist: {
|
||||||
|
integrity:
|
||||||
|
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||||
|
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||||
|
tarball: 'http://localhost:5555/protected-pkg/-/protected-pkg-5.0.5.tgz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readme: '# test',
|
||||||
|
_attachments: {
|
||||||
|
'protected-pkg-5.0.5.tgz': {
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
data:
|
||||||
|
'H4sIAAAAAAAAE+2W32vbMBDH85y/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo//79KPeQsnIw5KUDX/9IOvurLuz/DHSjK/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS/pLQe+D+FIv/agIWI6GX66kFuIhT+1gDjrp/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi/IHpU9fz3/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6/f88f/Pu47zomiPk2Lv/dOv8h+P/34/D/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=',
|
||||||
|
length: 512,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = json;
|
51
test/e2e-ui/partials/pkg-scoped.js
Normal file
51
test/e2e-ui/partials/pkg-scoped.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
const json = {
|
||||||
|
_id: '@scope/pk1-test',
|
||||||
|
name: '@scope/pk1-test',
|
||||||
|
description: '',
|
||||||
|
'dist-tags': {
|
||||||
|
latest: '1.0.6',
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
'1.0.6': {
|
||||||
|
name: '@scope/pk1-test',
|
||||||
|
version: '1.0.6',
|
||||||
|
description: '',
|
||||||
|
main: 'index.js',
|
||||||
|
scripts: {
|
||||||
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
|
},
|
||||||
|
keywords: [],
|
||||||
|
author: {
|
||||||
|
name: 'User NPM',
|
||||||
|
email: 'user@domain.com',
|
||||||
|
},
|
||||||
|
license: 'ISC',
|
||||||
|
dependencies: {
|
||||||
|
verdaccio: '^2.7.2',
|
||||||
|
},
|
||||||
|
readme: '# test',
|
||||||
|
readmeFilename: 'README.md',
|
||||||
|
_id: '@scope/pk1-test@1.0.6',
|
||||||
|
_npmVersion: '5.5.1',
|
||||||
|
_nodeVersion: '8.7.0',
|
||||||
|
_npmUser: {},
|
||||||
|
dist: {
|
||||||
|
integrity:
|
||||||
|
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||||
|
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||||
|
tarball: 'http://localhost:5555/@scope/pk1-test/-/@scope/pk1-test-1.0.6.tgz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readme: '# test',
|
||||||
|
_attachments: {
|
||||||
|
'@scope/pk1-test-1.0.6.tgz': {
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
data:
|
||||||
|
'H4sIAAAAAAAAE+2W32vbMBDH85y/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo//79KPeQsnIw5KUDX/9IOvurLuz/DHSjK/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS/pLQe+D+FIv/agIWI6GX66kFuIhT+1gDjrp/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi/IHpU9fz3/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6/f88f/Pu47zomiPk2Lv/dOv8h+P/34/D/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=',
|
||||||
|
length: 512,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = json;
|
4
test/e2e-ui/pre-setup.js
Normal file
4
test/e2e-ui/pre-setup.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
require('@babel/register')({
|
||||||
|
extensions: ['.ts', '.js'],
|
||||||
|
});
|
||||||
|
module.exports = require('./setup');
|
78
test/e2e-ui/puppeteer_environment.js
Normal file
78
test/e2e-ui/puppeteer_environment.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const NodeEnvironment = require('jest-environment-node');
|
||||||
|
const { yellow } = require('kleur');
|
||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
|
||||||
|
const VerdaccioProcess = require('./registry-launcher');
|
||||||
|
const Server = require('./server');
|
||||||
|
|
||||||
|
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
|
||||||
|
|
||||||
|
class VerdaccioConfig {
|
||||||
|
constructor(storagePath, configPath, domainPath, port) {
|
||||||
|
this.storagePath = storagePath;
|
||||||
|
this.configPath = configPath;
|
||||||
|
this.domainPath = domainPath;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PuppeteerEnvironment extends NodeEnvironment {
|
||||||
|
constructor(config) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup() {
|
||||||
|
const config1 = new VerdaccioConfig(
|
||||||
|
path.join(__dirname, './store-e2e'),
|
||||||
|
path.join(__dirname, './config/config-scoped-e2e.yaml'),
|
||||||
|
'http://0.0.0.0:55558/',
|
||||||
|
55558
|
||||||
|
);
|
||||||
|
const config2 = new VerdaccioConfig(
|
||||||
|
path.join(__dirname, './store-e2e'),
|
||||||
|
path.join(__dirname, './config/config-protected-e2e.yaml'),
|
||||||
|
'http://0.0.0.0:55552/',
|
||||||
|
55552
|
||||||
|
);
|
||||||
|
const server1 = new Server.default(config1.domainPath);
|
||||||
|
const server2 = new Server.default(config2.domainPath);
|
||||||
|
const process1 = new VerdaccioProcess.default(config1, server1);
|
||||||
|
const process2 = new VerdaccioProcess.default(config2, server2);
|
||||||
|
const verdaccioPath = path.normalize(
|
||||||
|
path.join(process.cwd(), '../../packages/verdaccio/debug/bootstrap.js')
|
||||||
|
);
|
||||||
|
const fork = await process1.init(verdaccioPath);
|
||||||
|
const fork2 = await process2.init(verdaccioPath);
|
||||||
|
this.global.__VERDACCIO_E2E__ = fork[0];
|
||||||
|
this.global.__VERDACCIO__PROTECTED_E2E__ = fork2[0];
|
||||||
|
|
||||||
|
console.log(yellow('Setup Test Environment.'));
|
||||||
|
await super.setup();
|
||||||
|
const wsEndpoint = fs.readFileSync(path.join(DIR, 'wsEndpoint'), 'utf8');
|
||||||
|
if (!wsEndpoint) {
|
||||||
|
throw new Error('wsEndpoint not found');
|
||||||
|
}
|
||||||
|
this.global.__SERVER__ = server1;
|
||||||
|
this.global.__SERVER_PROTECTED__ = server2;
|
||||||
|
this.global.__BROWSER__ = await puppeteer.connect({
|
||||||
|
browserWSEndpoint: wsEndpoint,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async teardown() {
|
||||||
|
console.log(yellow('Teardown Test Environment.'));
|
||||||
|
await super.teardown();
|
||||||
|
this.global.__VERDACCIO_E2E__.stop();
|
||||||
|
this.global.__VERDACCIO__PROTECTED_E2E__.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
runScript(script) {
|
||||||
|
return super.runScript(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PuppeteerEnvironment;
|
64
test/e2e-ui/registry-launcher.ts
Normal file
64
test/e2e-ui/registry-launcher.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { fork } from 'child_process';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||||
|
|
||||||
|
export const CREDENTIALS = {
|
||||||
|
user: 'foo',
|
||||||
|
password: 'test',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class VerdaccioProcess {
|
||||||
|
private bridge;
|
||||||
|
private config;
|
||||||
|
private childFork;
|
||||||
|
|
||||||
|
public constructor(config, bridge) {
|
||||||
|
this.config = config;
|
||||||
|
this.bridge = bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(verdaccioPath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._start(verdaccioPath, resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _start(verdaccioPath: string, resolve: Function, reject: Function) {
|
||||||
|
const verdaccioRegisterWrap: string = path.join(verdaccioPath);
|
||||||
|
const childOptions = {
|
||||||
|
silent: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { configPath, port } = this.config;
|
||||||
|
this.childFork = fork(
|
||||||
|
verdaccioRegisterWrap,
|
||||||
|
['-c', configPath, '-l', port as string],
|
||||||
|
childOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
this.childFork.on('message', (msg) => {
|
||||||
|
// verdaccio_started is a message that comes from verdaccio in debug mode that notify has been started
|
||||||
|
if ('verdaccio_started' in msg) {
|
||||||
|
this.bridge
|
||||||
|
.debug()
|
||||||
|
.status(HTTP_STATUS.OK)
|
||||||
|
.then((body) => {
|
||||||
|
this.bridge
|
||||||
|
.auth(CREDENTIALS.user, CREDENTIALS.password)
|
||||||
|
.status(HTTP_STATUS.CREATED)
|
||||||
|
.body_ok(new RegExp(CREDENTIALS.user))
|
||||||
|
.then(() => resolve([this, body.pid]), reject);
|
||||||
|
}, reject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.childFork.on('error', (err) => reject([err, this]));
|
||||||
|
this.childFork.on('disconnect', (err) => reject([err, this]));
|
||||||
|
this.childFork.on('exit', (err) => reject([err, this]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public stop(): void {
|
||||||
|
return this.childFork.kill('SIGINT');
|
||||||
|
}
|
||||||
|
}
|
137
test/e2e-ui/request.ts
Normal file
137
test/e2e-ui/request.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
import request from 'request';
|
||||||
|
|
||||||
|
const requestData = Symbol('smart_request_data');
|
||||||
|
|
||||||
|
export interface RequestPromise {
|
||||||
|
status(reason: any): any;
|
||||||
|
body_ok(reason: any): any;
|
||||||
|
body_error(reason: any): any;
|
||||||
|
request(reason: any): any;
|
||||||
|
response(reason: any): any;
|
||||||
|
send(reason: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectResponse(smartObject: any, promise: Promise<any>): Promise<any> {
|
||||||
|
// $FlowFixMe
|
||||||
|
promise[requestData] = smartObject[requestData];
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PromiseAssert extends Promise<any> implements RequestPromise {
|
||||||
|
public constructor(options: any) {
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public status(expected: number) {
|
||||||
|
const selfData = this[requestData];
|
||||||
|
|
||||||
|
return injectResponse(
|
||||||
|
this,
|
||||||
|
this.then(function (body) {
|
||||||
|
try {
|
||||||
|
assert.equal(selfData.response.statusCode, expected);
|
||||||
|
} catch (err) {
|
||||||
|
selfData.error.message = err.message;
|
||||||
|
throw selfData.error;
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public body_ok(expected: any) {
|
||||||
|
const selfData = this[requestData];
|
||||||
|
|
||||||
|
return injectResponse(
|
||||||
|
this,
|
||||||
|
this.then(function (body) {
|
||||||
|
try {
|
||||||
|
if (_.isRegExp(expected)) {
|
||||||
|
assert(body.ok.match(expected), "'" + body.ok + "' doesn't match " + expected);
|
||||||
|
} else {
|
||||||
|
assert.equal(body.ok, expected);
|
||||||
|
}
|
||||||
|
assert.equal(body.error, null);
|
||||||
|
} catch (err) {
|
||||||
|
selfData.error.message = err.message;
|
||||||
|
throw selfData.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public body_error(expected: any) {
|
||||||
|
// $FlowFixMe
|
||||||
|
const selfData = this[requestData];
|
||||||
|
|
||||||
|
return injectResponse(
|
||||||
|
this,
|
||||||
|
this.then(function (body) {
|
||||||
|
try {
|
||||||
|
if (_.isRegExp(expected)) {
|
||||||
|
assert(body.error.match(expected), body.error + " doesn't match " + expected);
|
||||||
|
} else {
|
||||||
|
assert.equal(body.error, expected);
|
||||||
|
}
|
||||||
|
assert.equal(body.ok, null);
|
||||||
|
} catch (err) {
|
||||||
|
selfData.error.message = err.message;
|
||||||
|
throw selfData.error;
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(callback: any) {
|
||||||
|
callback(this[requestData].request);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public response(cb: any) {
|
||||||
|
const selfData = this[requestData];
|
||||||
|
|
||||||
|
return injectResponse(
|
||||||
|
this,
|
||||||
|
this.then(function (body) {
|
||||||
|
cb(selfData.response);
|
||||||
|
return body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public send(data: any) {
|
||||||
|
this[requestData].request.end(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function smartRequest(options: any): Promise<any> {
|
||||||
|
const smartObject: any = {};
|
||||||
|
|
||||||
|
smartObject[requestData] = {};
|
||||||
|
smartObject[requestData].error = Error();
|
||||||
|
Error.captureStackTrace(smartObject[requestData].error, smartRequest);
|
||||||
|
|
||||||
|
const promiseResult: Promise<any> = new PromiseAssert(function (resolve, reject) {
|
||||||
|
// store request reference on symbol
|
||||||
|
smartObject[requestData].request = request(options, function (err, res, body) {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the response on symbol
|
||||||
|
smartObject[requestData].response = res;
|
||||||
|
resolve(body);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return injectResponse(smartObject, promiseResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default smartRequest;
|
276
test/e2e-ui/server.ts
Normal file
276
test/e2e-ui/server.ts
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import { HTTP_STATUS, HEADERS, API_MESSAGE } from '@verdaccio/commons-api';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { CREDENTIALS } from './registry-launcher';
|
||||||
|
import smartRequest, { RequestPromise } from './request';
|
||||||
|
|
||||||
|
declare class PromiseAssert<RequestPromise> extends Promise<any> {
|
||||||
|
public constructor(options: any);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TARBALL = 'tarball-blahblah-file.name';
|
||||||
|
|
||||||
|
function getPackage(name, version = '0.0.0'): any {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
readme: 'this is a readme',
|
||||||
|
dist: {
|
||||||
|
shasum: 'fake',
|
||||||
|
tarball: `http://localhost:0000/${encodeURIComponent(name)}/-/${TARBALL}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServerBridge {
|
||||||
|
url: string;
|
||||||
|
userAgent: string;
|
||||||
|
authstr: string;
|
||||||
|
request(options: any): typeof PromiseAssert;
|
||||||
|
auth(name: string, password: string): RequestPromise;
|
||||||
|
auth(name: string, password: string): RequestPromise;
|
||||||
|
logout(token: string): Promise<any>;
|
||||||
|
getPackage(name: string): Promise<any>;
|
||||||
|
putPackage(name: string, data: any): Promise<any>;
|
||||||
|
putVersion(name: string, version: string, data: any): Promise<any>;
|
||||||
|
getTarball(name: string, filename: string): Promise<any>;
|
||||||
|
putTarball(name: string, filename: string, data: any): Promise<any>;
|
||||||
|
removeTarball(name: string): Promise<any>;
|
||||||
|
removeSingleTarball(name: string, filename: string): Promise<any>;
|
||||||
|
addTag(name: string, tag: string, version: string): Promise<any>;
|
||||||
|
putTarballIncomplete(
|
||||||
|
name: string,
|
||||||
|
filename: string,
|
||||||
|
data: any,
|
||||||
|
size: number,
|
||||||
|
cb: Function
|
||||||
|
): Promise<any>;
|
||||||
|
addPackage(name: string): Promise<any>;
|
||||||
|
whoami(): Promise<any>;
|
||||||
|
ping(): Promise<any>;
|
||||||
|
debug(): RequestPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TOKEN_BASIC = 'Basic';
|
||||||
|
|
||||||
|
function buildToken(type: string, token: string): string {
|
||||||
|
return `${_.capitalize(type)} ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildAuthHeader = (user, pass): string => {
|
||||||
|
return buildToken(TOKEN_BASIC, new Buffer(`${user}:${pass}`).toString('base64'));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Server implements ServerBridge {
|
||||||
|
public url: string;
|
||||||
|
public userAgent: string;
|
||||||
|
public authstr: string;
|
||||||
|
|
||||||
|
public constructor(url: string) {
|
||||||
|
this.url = url.replace(/\/$/, '');
|
||||||
|
this.userAgent = 'node/v8.1.2 linux x64';
|
||||||
|
this.authstr = buildAuthHeader(CREDENTIALS.user, CREDENTIALS.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(options: any): any {
|
||||||
|
assert(options.uri);
|
||||||
|
const headers = options.headers || {};
|
||||||
|
|
||||||
|
headers.accept = headers.accept || HEADERS.JSON;
|
||||||
|
headers['user-agent'] = headers['user-agent'] || this.userAgent;
|
||||||
|
headers.authorization = headers.authorization || this.authstr;
|
||||||
|
|
||||||
|
return smartRequest({
|
||||||
|
url: this.url + options.uri,
|
||||||
|
method: options.method || 'GET',
|
||||||
|
headers: headers,
|
||||||
|
encoding: options.encoding,
|
||||||
|
json: _.isNil(options.json) === false ? options.json : true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public auth(name: string, password: string) {
|
||||||
|
// pragma: allowlist secret
|
||||||
|
this.authstr = buildAuthHeader(name, password);
|
||||||
|
return this.request({
|
||||||
|
uri: `/-/user/org.couchdb.user:${encodeURIComponent(name)}/-rev/undefined`,
|
||||||
|
method: 'PUT',
|
||||||
|
json: {
|
||||||
|
name,
|
||||||
|
password,
|
||||||
|
email: `${CREDENTIALS.user}@example.com`,
|
||||||
|
_id: `org.couchdb.user:${name}`,
|
||||||
|
type: 'user',
|
||||||
|
roles: [],
|
||||||
|
date: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public logout(token: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/-/user/token/${encodeURIComponent(token)}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPackage(name: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public putPackage(name: string, data) {
|
||||||
|
if (_.isObject(data) && !Buffer.isBuffer(data)) {
|
||||||
|
data = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON,
|
||||||
|
},
|
||||||
|
}).send(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public putVersion(name: string, version: string, data: any) {
|
||||||
|
if (_.isObject(data) && !Buffer.isBuffer(data)) {
|
||||||
|
data = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/${encodeURIComponent(version)}/-tag/latest`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON,
|
||||||
|
},
|
||||||
|
}).send(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTarball(name: string, filename: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}`,
|
||||||
|
method: 'GET',
|
||||||
|
encoding: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public putTarball(name: string, filename: string, data: any) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}/whatever`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.OCTET_STREAM,
|
||||||
|
},
|
||||||
|
}).send(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeTarball(name: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/-rev/whatever`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON_CHARSET,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeSingleTarball(name: string, filename: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/-/${filename}/-rev/whatever`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON_CHARSET,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addTag(name: string, tag: string, version: string) {
|
||||||
|
return this.request({
|
||||||
|
uri: `/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON,
|
||||||
|
},
|
||||||
|
}).send(JSON.stringify(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
public putTarballIncomplete(
|
||||||
|
pkgName: string,
|
||||||
|
filename: string,
|
||||||
|
data: any,
|
||||||
|
headerContentSize: number
|
||||||
|
): Promise<any> {
|
||||||
|
const promise = this.request({
|
||||||
|
uri: `/${encodeURIComponent(pkgName)}/-/${encodeURIComponent(filename)}/whatever`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.OCTET_STREAM,
|
||||||
|
[HEADERS.CONTENT_LENGTH]: headerContentSize,
|
||||||
|
},
|
||||||
|
timeout: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.request(function (req) {
|
||||||
|
req.write(data);
|
||||||
|
// it auto abort the request
|
||||||
|
setTimeout(function () {
|
||||||
|
req.req.abort();
|
||||||
|
}, 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
promise
|
||||||
|
.then(function () {
|
||||||
|
reject(Error('no error'));
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
if (err.code === 'ECONNRESET') {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addPackage(name: string) {
|
||||||
|
return this.putPackage(name, getPackage(name))
|
||||||
|
.status(HTTP_STATUS.CREATED)
|
||||||
|
.body_ok(API_MESSAGE.PKG_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public whoami() {
|
||||||
|
return this.request({
|
||||||
|
uri: '/-/whoami',
|
||||||
|
})
|
||||||
|
.status(HTTP_STATUS.OK)
|
||||||
|
.then(function (body) {
|
||||||
|
return body.username;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ping() {
|
||||||
|
return this.request({
|
||||||
|
uri: '/-/ping',
|
||||||
|
})
|
||||||
|
.status(HTTP_STATUS.OK)
|
||||||
|
.then(function (body) {
|
||||||
|
return body;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public debug() {
|
||||||
|
return this.request({
|
||||||
|
uri: '/-/_debug',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
[HEADERS.CONTENT_TYPE]: HEADERS.JSON,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
22
test/e2e-ui/setup.js
Normal file
22
test/e2e-ui/setup.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const { green } = require('kleur');
|
||||||
|
const mkdirp = require('mkdirp');
|
||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
|
||||||
|
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
|
||||||
|
|
||||||
|
module.exports = async function () {
|
||||||
|
console.log(green('Setup Puppeteer'));
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: true,
|
||||||
|
// slowMo: 600,
|
||||||
|
// devtools: true,
|
||||||
|
args: ['--no-sandbox'],
|
||||||
|
});
|
||||||
|
global.__BROWSER__ = browser;
|
||||||
|
mkdirp.sync(DIR);
|
||||||
|
fs.writeFileSync(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
|
||||||
|
};
|
13
test/e2e-ui/teardown.js
Normal file
13
test/e2e-ui/teardown.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const { green } = require('kleur');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
|
||||||
|
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
|
||||||
|
|
||||||
|
module.exports = async function () {
|
||||||
|
console.log(green('Teardown Puppeteer'));
|
||||||
|
await global.__BROWSER__.close();
|
||||||
|
rimraf.sync(DIR);
|
||||||
|
};
|
|
@ -17,6 +17,9 @@
|
||||||
"babel-preset-gatsby": "^0.4.12",
|
"babel-preset-gatsby": "^0.4.12",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"emotion-theming": "10.0.27",
|
"emotion-theming": "10.0.27",
|
||||||
|
"prop-types": "15.7.2",
|
||||||
|
"react": "16.13.1",
|
||||||
|
"react-dom": "16.13.1",
|
||||||
"event-source-polyfill": "^1.0.17",
|
"event-source-polyfill": "^1.0.17",
|
||||||
"fontsource-roboto": "^2.2.6",
|
"fontsource-roboto": "^2.2.6",
|
||||||
"gatsby": "^2.24.51",
|
"gatsby": "^2.24.51",
|
||||||
|
@ -40,10 +43,7 @@
|
||||||
"lisan": "^0.1.1",
|
"lisan": "^0.1.1",
|
||||||
"mitt": "2.1.0",
|
"mitt": "2.1.0",
|
||||||
"prismjs": "^1.21.0",
|
"prismjs": "^1.21.0",
|
||||||
"prop-types": "15.7.2",
|
|
||||||
"query-string": "^6.13.1",
|
"query-string": "^6.13.1",
|
||||||
"react": "16.13.1",
|
|
||||||
"react-dom": "16.13.1",
|
|
||||||
"react-error-overlay": "^6.0.7",
|
"react-error-overlay": "^6.0.7",
|
||||||
"react-helmet": "5.2.1",
|
"react-helmet": "5.2.1",
|
||||||
"react-twitter-widgets": "^1.9.5",
|
"react-twitter-widgets": "^1.9.5",
|
||||||
|
|
Loading…
Reference in a new issue