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
|
||||
|
||||
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:
|
||||
ci:
|
||||
|
@ -16,12 +28,18 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2.3.1
|
||||
- name: Use Node ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v2.1.5
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
node_version: ${{ matrix.node_version }}
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm@latest
|
||||
- name: Install
|
||||
run: yarn install --immutable
|
||||
run: pnpm recursive install
|
||||
- name: Clean
|
||||
run: pnpm clean
|
||||
- name: Build
|
||||
run: yarn code:build
|
||||
run: pnpm build
|
||||
- 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:
|
||||
push:
|
||||
|
@ -7,12 +7,12 @@ on:
|
|||
- 'changeset-release/5.x'
|
||||
pull_request:
|
||||
paths:
|
||||
- .changeset/**
|
||||
- .github/workflows/ci.yml
|
||||
- 'packages/**'
|
||||
- 'jest/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'test/**'
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
|
@ -39,7 +39,7 @@ jobs:
|
|||
run: pnpm clean
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
- name: Test
|
||||
- name: Test CLI
|
||||
run: pnpm test:e2e:cli
|
||||
env:
|
||||
DEBUG: verdaccio:e2e*
|
||||
|
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
|
@ -5,7 +5,11 @@ on:
|
|||
branches-ignore:
|
||||
- 5.x
|
||||
- 'changeset-release/5.x'
|
||||
- 'dev/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/ci.yml
|
||||
- 'packages/**'
|
||||
schedule:
|
||||
- cron: '0 2 * * 4'
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
CHANGELOG.md
|
||||
CONTRIBUTORS.md
|
||||
node_modules/
|
||||
**/coverage/**
|
||||
**/static/*.js
|
||||
**/build/*.js
|
||||
packages/core/local-storage/_storage/**
|
||||
|
|
|
@ -128,6 +128,7 @@
|
|||
"lint": "eslint \"**/*.{js,jsx,ts,tsx}\"",
|
||||
"test": "pnpm recursive test --filter ./packages",
|
||||
"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:server": "node packages/verdaccio/debug/bootstrap.js --listen 8000",
|
||||
"start:web": "pnpm start --filter ...@verdaccio/ui-theme",
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"@testing-library/dom": "^7.29.0",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "10.4.9",
|
||||
"@verdaccio/commons-api": "workspace:10.0.0-alpha.1",
|
||||
"@verdaccio/node-api": "workspace:5.0.0-alpha.1",
|
||||
"autosuggest-highlight": "3.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
|
@ -56,11 +55,7 @@
|
|||
"normalize.css": "8.0.1",
|
||||
"optimize-css-assets-webpack-plugin": "^5.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-dom": "16.13.1",
|
||||
"react-hook-form": "3.29.4",
|
||||
"react-hot-loader": "4.12.21",
|
||||
"react-i18next": "^11.8.3",
|
||||
|
@ -70,7 +65,10 @@
|
|||
"request": "2.88.2",
|
||||
"resolve-url-loader": "3.1.1",
|
||||
"rimraf": "3.0.2",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"source-map-loader": "1.1.0",
|
||||
"prop-types": "15.7.2",
|
||||
"standard-version": "9.0.0",
|
||||
"style-loader": "1.2.1",
|
||||
"stylelint": "13.7.2",
|
||||
|
@ -124,7 +122,6 @@
|
|||
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
||||
"start": "babel-node tools/dev.server.js",
|
||||
"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:update-snapshot": "yarn run test -- -u",
|
||||
"test:size": "bundlesize",
|
||||
|
|
|
@ -640,7 +640,6 @@ importers:
|
|||
'@testing-library/dom': 7.29.0
|
||||
'@testing-library/jest-dom': 5.11.6
|
||||
'@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'
|
||||
autosuggest-highlight: 3.1.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
|
||||
ora: 4.0.4
|
||||
prop-types: 15.7.2
|
||||
puppeteer: 5.3.1
|
||||
react: 16.13.1
|
||||
react-autosuggest: 10.0.2_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/jest-dom': ^5.11.6
|
||||
'@testing-library/react': 10.4.9
|
||||
'@verdaccio/commons-api': 'workspace:10.0.0-alpha.1'
|
||||
'@verdaccio/node-api': 'workspace:5.0.0-alpha.1'
|
||||
autosuggest-highlight: 3.1.1
|
||||
babel-loader: ^8.2.2
|
||||
|
@ -752,7 +749,6 @@ importers:
|
|||
optimize-css-assets-webpack-plugin: ^5.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-dom: 16.13.1
|
||||
|
@ -982,6 +978,27 @@ importers:
|
|||
request: ^2.88.2
|
||||
semver: ^7.3.4
|
||||
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:
|
||||
dependencies:
|
||||
'@emotion/core': 10.0.28_react@16.13.1
|
||||
|
@ -11291,10 +11308,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-7/nIzKdQ8y2K0imjIP7dyg2GJ2h38Ps6VOMXWZHIarNDV3p6mTXyEugKFnkmsZ2DD58JEG34ILyVb3qdOMmP9w==
|
||||
/devtools-protocol/0.0.799653:
|
||||
/devtools-protocol/0.0.818844:
|
||||
dev: true
|
||||
resolution:
|
||||
integrity: sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==
|
||||
integrity: sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==
|
||||
/diacritic/0.0.2:
|
||||
dev: true
|
||||
resolution:
|
||||
|
@ -17586,6 +17603,12 @@ packages:
|
|||
node: '>=6'
|
||||
resolution:
|
||||
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:
|
||||
dev: true
|
||||
resolution:
|
||||
|
@ -21506,12 +21529,13 @@ packages:
|
|||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==
|
||||
/puppeteer/5.3.1:
|
||||
/puppeteer/5.5.0:
|
||||
dependencies:
|
||||
debug: 4.3.1
|
||||
devtools-protocol: 0.0.799653
|
||||
devtools-protocol: 0.0.818844
|
||||
extract-zip: 2.0.1
|
||||
https-proxy-agent: 4.0.0
|
||||
node-fetch: 2.6.1
|
||||
pkg-dir: 4.2.0
|
||||
progress: 2.0.3
|
||||
proxy-from-env: 1.1.0
|
||||
|
@ -21524,7 +21548,7 @@ packages:
|
|||
node: '>=10.18.1'
|
||||
requiresBuild: true
|
||||
resolution:
|
||||
integrity: sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==
|
||||
integrity: sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==
|
||||
/q/1.5.1:
|
||||
engines:
|
||||
node: '>=0.6.0'
|
||||
|
|
|
@ -3,4 +3,4 @@ packages:
|
|||
- packages/core/*
|
||||
- packages/plugins/*
|
||||
- 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?
|
||||
|
||||
|
|
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",
|
||||
"classnames": "^2.2.6",
|
||||
"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",
|
||||
"fontsource-roboto": "^2.2.6",
|
||||
"gatsby": "^2.24.51",
|
||||
|
@ -40,10 +43,7 @@
|
|||
"lisan": "^0.1.1",
|
||||
"mitt": "2.1.0",
|
||||
"prismjs": "^1.21.0",
|
||||
"prop-types": "15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-error-overlay": "^6.0.7",
|
||||
"react-helmet": "5.2.1",
|
||||
"react-twitter-widgets": "^1.9.5",
|
||||
|
|
Loading…
Reference in a new issue