1
0
mirror of https://github.com/SomboChea/ui synced 2026-01-12 06:05:43 +07:00

build: e2e integration with puppeteer (#192)

* build: add e2e testing scripts

* build: add e2e testing scripts

* chore: fix script

* chore: fix script

* chore: ignore e2e normal test

* chore: fix node_latest_browser

* chore: move lint to prepare

* chore: fix lint

* chore: add local theme

* fix: e2e tests
This commit is contained in:
Juan Picado @jotadeveloper
2019-10-13 10:27:01 +02:00
committed by GitHub
parent 0c4fb7da13
commit d1b3e6e3b5
19 changed files with 1328 additions and 142 deletions

11
test/e2e/.eslintrc Normal file
View File

@@ -0,0 +1,11 @@
{
"rules": {
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-member-accessibility": 0,
"no-console": 0,
"new-cap": 0,
"max-len": 0
}
}

View File

@@ -0,0 +1,31 @@
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: warn }
packages:
'protected-*':
access: $authenticated
publish: $authenticated
theme:
../plugins/theme:
prop: dummy
listen: 0.0.0.0:55552
# expose internal methods
_debug: true

View File

@@ -0,0 +1,34 @@
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: warn }
packages:
'@*/*':
access: $all
publish: $all
'**':
access: $all
publish: $authenticated
theme:
../plugins/theme:
prop: dummy
listen: 0.0.0.0:55558
# expose internal methods
_debug: true

193
test/e2e/e2e.spec.js Normal file
View File

@@ -0,0 +1,193 @@
const scopedPackageMetadata = require('./partials/pkg-scoped');
const protectedPackageMetadata = require('./partials/pkg-protected');
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 = { button: 'middle', delay: 100 }) {
const button = await page.$(selector);
await button.focus();
await button.click(options);
};
const evaluateSignIn = async function() {
const text = await page.evaluate(() => document.querySelector('button[data-testid="header--button-login"]').textContent);
expect(text).toMatch('Login');
};
const getPackages = async function() {
return await page.$$('.package-list-items .package-link a');
};
const logIn = async function() {
await clickElement('button[data-testid="header--button-login"]');
await page.waitFor(500);
// we fill the sign in form
const signInDialog = await page.$('#login--form-container');
const userInput = await signInDialog.$('#login--form-username');
expect(userInput).not.toBeNull();
const passInput = await signInDialog.$('#login--form-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--form-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 load without error', async () => {
const text = await page.evaluate(() => document.body.textContent);
// FIXME: perhaps it is not the best approach
expect(text).toContain('Powered by');
});
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--form-container');
expect(signInDialog).not.toBeNull();
});
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', { clickCount: 1, delay: 500 });
await page.waitFor(1000);
await clickElement('#header--button-logout > span', { clickCount: 1, 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');
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();
const firstPackage = packagesList[0];
await firstPackage.click({ clickCount: 1, 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({ clickCount: 1, 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({ clickCount: 1, 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({ clickCount: 1, 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({ clickCount: 1, 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...");
});
});

View File

@@ -0,0 +1,50 @@
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;

View File

@@ -0,0 +1,50 @@
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;

View File

@@ -0,0 +1,6 @@
const path = require('path');
module.exports = () => {
console.log('loading local theme');
return path.join(__dirname, '../../../', 'static');
};

4
test/e2e/pre-setup.js Normal file
View File

@@ -0,0 +1,4 @@
require('@babel/register')({
extensions: ['.ts', '.js'],
});
module.exports = require('./setup');

View File

@@ -0,0 +1,75 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const { yellow } = require('kleur');
const NodeEnvironment = require('jest-environment-node');
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 fork = await process1.init('../../node_modules/.bin/verdaccio');
const fork2 = await process2.init('../../node_modules/.bin/verdaccio');
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;

View File

@@ -0,0 +1,60 @@
import path from 'path';
import { fork } from 'child_process';
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(__dirname, 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/request.ts Normal file
View 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;

265
test/e2e/server.ts Normal file
View File

@@ -0,0 +1,265 @@
import assert from 'assert';
import _ from 'lodash';
import { HTTP_STATUS, HEADERS, API_MESSAGE } from '@verdaccio/commons-api';
import smartRequest, { RequestPromise } from './request';
import { CREDENTIALS } from './registry-launcher';
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,
},
});
}
}

17
test/e2e/setup.js Normal file
View File

@@ -0,0 +1,17 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const { green } = require('kleur');
const puppeteer = require('puppeteer');
const mkdirp = require('mkdirp');
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: 300 */ args: ['--no-sandbox'] });
global.__BROWSER__ = browser;
mkdirp.sync(DIR);
fs.writeFileSync(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
};

13
test/e2e/teardown.js Normal file
View 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);
};

27
test/jest.config.e2e.js Normal file
View File

@@ -0,0 +1,27 @@
module.exports = {
name: 'verdaccio-e2e-jest',
verbose: true,
collectCoverage: false,
globalSetup: './e2e/pre-setup.js',
globalTeardown: './e2e/teardown.js',
testEnvironment: './e2e/puppeteer_environment.js',
testRegex: '(/e2e.*\\.spec)\\.js',
modulePathIgnorePatterns: [
'<rootDir>/unit/partials/mock-store/.*/package.json',
'<rootDir>/functional/store/.*/package.json',
'<rootDir>/unit/partials/store/.*/package.json',
'<rootDir>/../coverage',
'<rootDir>/../docs',
'<rootDir>/../debug',
'<rootDir>/../scripts',
'<rootDir>/../.circleci',
'<rootDir>/../tools',
'<rootDir>/../wiki',
'<rootDir>/../systemd',
'<rootDir>/../flow-typed',
'<rootDir>unit/partials/mock-store/.*/package.json',
'<rootDir>functional/store/.*/package.json',
'<rootDir>/../build',
'<rootDir>/../.vscode/',
],
};