feat: allow disable login on ui (#2228)

This commit is contained in:
Juan Picado 2021-05-05 23:23:03 +02:00 committed by GitHub
parent 393125baa1
commit 0da7031e77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 168 additions and 67 deletions

View File

@ -0,0 +1,15 @@
---
'@verdaccio/types': minor
'@verdaccio/ui-theme': minor
'@verdaccio/web': minor
---
allow disable login on ui and endpoints
To be able disable the login, set `login: false`, anything else would enable login. This flag will disable access via UI and web endpoints.
```yml
web:
title: verdaccio
login: false
```

View File

@ -27,6 +27,7 @@ declare module '@verdaccio/types' {
darkMode?: boolean; darkMode?: boolean;
url_prefix?: string; url_prefix?: string;
language?: string; language?: string;
login?: boolean;
scope?: string; scope?: string;
pkgManagers?: PackageManagers[]; pkgManagers?: PackageManagers[];
}; };

View File

@ -5,6 +5,7 @@ import {
render, render,
fireEvent, fireEvent,
waitFor, waitFor,
screen,
waitForElementToBeRemoved, waitForElementToBeRemoved,
} from 'verdaccio-ui/utils/test-react-testing-library'; } from 'verdaccio-ui/utils/test-react-testing-library';
@ -23,7 +24,7 @@ const props = {
/* eslint-disable react/jsx-no-bind*/ /* eslint-disable react/jsx-no-bind*/
describe('<Header /> component with logged in state', () => { describe('<Header /> component with logged in state', () => {
test('should load the component in logged out state', () => { test('should load the component in logged out state', () => {
const { queryByTestId, getByText } = render( render(
<Router> <Router>
<AppContextProvider> <AppContextProvider>
<Header /> <Header />
@ -31,8 +32,9 @@ describe('<Header /> component with logged in state', () => {
</Router> </Router>
); );
expect(queryByTestId('header--menu-accountcircle')).toBeNull(); expect(screen.queryByTestId('header--menu-accountcircle')).toBeNull();
expect(getByText('Login')).toBeTruthy(); expect(screen.getByText('Login')).toBeTruthy();
expect(screen.queryByTestId('header--button-login')).toBeInTheDocument();
}); });
test('should load the component in logged in state', () => { test('should load the component in logged in state', () => {
@ -135,5 +137,22 @@ describe('<Header /> component with logged in state', () => {
expect(hasRegistrationInfoModalBeenRemoved).not.toBeDefined(); expect(hasRegistrationInfoModalBeenRemoved).not.toBeDefined();
}); });
test('should hide login if is disabled', () => {
// @ts-expect-error
window.__VERDACCIO_BASENAME_UI_OPTIONS = {
base: 'foo',
login: false,
};
render(
<Router>
<AppContextProvider user={props.user}>
<Header />
</AppContextProvider>
</Router>
);
expect(screen.queryByTestId('header--button-login')).not.toBeInTheDocument();
});
test.todo('autocompletion should display suggestions according to the type value'); test.todo('autocompletion should display suggestions according to the type value');
}); });

View File

@ -49,6 +49,7 @@ const Header: React.FC<Props> = ({ withoutSearch }) => {
<InnerNavBar> <InnerNavBar>
<HeaderLeft /> <HeaderLeft />
<HeaderRight <HeaderRight
hasLogin={configOptions?.login}
onLogout={handleLogout} onLogout={handleLogout}
onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)} onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)}
onToggleLogin={() => setShowLoginModal(!showLoginModal)} onToggleLogin={() => setShowLoginModal(!showLoginModal)}

View File

@ -12,6 +12,7 @@ import { RightSide } from './styles';
interface Props { interface Props {
withoutSearch?: boolean; withoutSearch?: boolean;
username?: string | null; username?: string | null;
hasLogin?: boolean;
onToggleLogin: () => void; onToggleLogin: () => void;
onOpenRegistryInfoDialog: () => void; onOpenRegistryInfoDialog: () => void;
onToggleMobileNav: () => void; onToggleMobileNav: () => void;
@ -22,6 +23,7 @@ const HeaderRight: React.FC<Props> = ({
withoutSearch = false, withoutSearch = false,
username, username,
onToggleLogin, onToggleLogin,
hasLogin,
onLogout, onLogout,
onToggleMobileNav, onToggleMobileNav,
onOpenRegistryInfoDialog, onOpenRegistryInfoDialog,
@ -29,6 +31,7 @@ const HeaderRight: React.FC<Props> = ({
const themeContext = useContext(ThemeContext); const themeContext = useContext(ThemeContext);
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null); const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false); const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
const hideLoginSection = hasLogin === false;
const { t } = useTranslation(); const { t } = useTranslation();
@ -90,19 +93,23 @@ const HeaderRight: React.FC<Props> = ({
tooltipIconType={themeContext.isDarkMode ? 'dark-mode' : 'light-mode'} tooltipIconType={themeContext.isDarkMode ? 'dark-mode' : 'light-mode'}
/> />
{username ? ( {!hideLoginSection && (
<HeaderMenu <>
anchorEl={anchorEl} {username ? (
isMenuOpen={isMenuOpen} <HeaderMenu
onLoggedInMenu={handleLoggedInMenu} anchorEl={anchorEl}
onLoggedInMenuClose={handleLoggedInMenuClose} isMenuOpen={isMenuOpen}
onLogout={onLogout} onLoggedInMenu={handleLoggedInMenu}
username={username} onLoggedInMenuClose={handleLoggedInMenuClose}
/> onLogout={onLogout}
) : ( username={username}
<Button color="inherit" data-testid="header--button-login" onClick={handleToggleLogin}> />
{t('button.login')} ) : (
</Button> <Button color="inherit" data-testid="header--button-login" onClick={handleToggleLogin}>
{t('button.login')}
</Button>
)}
</>
)} )}
</RightSide> </RightSide>
); );

View File

@ -17,6 +17,7 @@ const defaultValues: ConfigProviderProps = {
pkgManagers: ['yarn', 'pnpm', 'npm'], pkgManagers: ['yarn', 'pnpm', 'npm'],
scope: '', scope: '',
base: '', base: '',
login: true,
url_prefix: '', url_prefix: '',
title: 'Verdaccio', title: 'Verdaccio',
}, },
@ -46,7 +47,6 @@ const AppConfigurationProvider: FunctionComponent = ({ children }) => {
[configOptions] [configOptions]
); );
// @ts-ignore
return ( return (
<AppConfigurationContext.Provider value={value}>{children}</AppConfigurationContext.Provider> <AppConfigurationContext.Provider value={value}>{children}</AppConfigurationContext.Provider>
); );

View File

@ -2,6 +2,7 @@ web:
title: Verdaccio Local Dev title: Verdaccio Local Dev
sort_packages: asc sort_packages: asc
primary_color: #CCC primary_color: #CCC
login: true
pkgManagers: pkgManagers:
- npm - npm
- yarn - yarn

View File

@ -9,6 +9,7 @@ export interface VerdaccioOptions {
primaryColor: string; primaryColor: string;
darkMode: boolean; darkMode: boolean;
uri?: string; uri?: string;
login?: boolean;
language?: string; language?: string;
version?: string; version?: string;
protocol?: string; protocol?: string;

View File

@ -29,11 +29,20 @@ function addPackageWebApi(
auth: IAuth, auth: IAuth,
config: Config config: Config
): void { ): void {
const isLoginEnabled = config?.web?.login === true ?? true;
const anonymousRemoteUser: RemoteUser = {
name: undefined,
real_groups: [],
groups: [],
};
debug('initialized package web api'); debug('initialized package web api');
const checkAllow = (name: string, remoteUser: RemoteUser): Promise<boolean> => const checkAllow = (name: string, remoteUser: RemoteUser): Promise<boolean> =>
new Promise((resolve, reject): void => { new Promise((resolve, reject): void => {
debug('is login disabled %o', isLoginEnabled);
const remoteUserAccess = !isLoginEnabled ? anonymousRemoteUser : remoteUser;
try { try {
auth.allow_access({ packageName: name }, remoteUser, (err, allowed): void => { auth.allow_access({ packageName: name }, remoteUserAccess, (err, allowed): void => {
if (err) { if (err) {
resolve(false); resolve(false);
} }

View File

@ -37,7 +37,7 @@ function addReadmeWebApi(route: Router, storage: IStorageHandler, auth: IAuth):
uplinksLook: true, uplinksLook: true,
req, req,
callback: function (err, info): void { callback: function (err, info): void {
debug('readme plg %o', info); debug('readme plg %o', info?.name);
if (err) { if (err) {
return next(err); return next(err);
} }

View File

@ -11,6 +11,7 @@ import addPackageWebApi from '../api/package';
import addUserAuthApi from '../api/user'; import addUserAuthApi from '../api/user';
import addReadmeWebApi from '../api/readme'; import addReadmeWebApi from '../api/readme';
import addSidebarWebApi from '../api/sidebar'; import addSidebarWebApi from '../api/sidebar';
import { hasLogin } from '../utils/web-utils';
import { setSecurityWebHeaders } from './security'; import { setSecurityWebHeaders } from './security';
export function webAPI(config: Config, auth: IAuth, storage: IStorageHandler): Router { export function webAPI(config: Config, auth: IAuth, storage: IStorageHandler): Router {
@ -33,7 +34,9 @@ export function webAPI(config: Config, auth: IAuth, storage: IStorageHandler): R
addReadmeWebApi(route, storage, auth); addReadmeWebApi(route, storage, auth);
addSidebarWebApi(route, config, storage, auth); addSidebarWebApi(route, config, storage, auth);
addSearchWebApi(route, storage, auth); addSearchWebApi(route, storage, auth);
addUserAuthApi(route, auth, config); if (hasLogin(config)) {
addUserAuthApi(route, auth, config);
}
// What are you looking for? logout? client side will remove token when user click logout, // What are you looking for? logout? client side will remove token when user click logout,
// or it will auto expire after 24 hours. // or it will auto expire after 24 hours.
// This token is different with the token send to npm client. // This token is different with the token send to npm client.

View File

@ -5,7 +5,7 @@ import { HEADERS } from '@verdaccio/commons-api';
import { getPublicUrl } from '@verdaccio/url'; import { getPublicUrl } from '@verdaccio/url';
import { WEB_TITLE } from '@verdaccio/config'; import { WEB_TITLE } from '@verdaccio/config';
import { validatePrimaryColor } from './utils/web-utils'; import { hasLogin, validatePrimaryColor } from './utils/web-utils';
import renderTemplate from './template'; import renderTemplate from './template';
const pkgJSON = require('../package.json'); const pkgJSON = require('../package.json');
@ -26,6 +26,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
const language = config?.i18n?.web ?? DEFAULT_LANGUAGE; const language = config?.i18n?.web ?? DEFAULT_LANGUAGE;
const darkMode = config?.web?.darkMode ?? false; const darkMode = config?.web?.darkMode ?? false;
const title = config?.web?.title ?? WEB_TITLE; const title = config?.web?.title ?? WEB_TITLE;
const login = hasLogin(config);
const scope = config?.web?.scope ?? ''; const scope = config?.web?.scope ?? '';
// FIXME: logo URI is incomplete // FIXME: logo URI is incomplete
let logoURI = config?.web?.logo ?? ''; let logoURI = config?.web?.logo ?? '';
@ -49,6 +50,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
primaryColor, primaryColor,
version, version,
logoURI, logoURI,
login,
pkgManagers, pkgManagers,
title, title,
scope, scope,

View File

@ -1,7 +1,7 @@
import _ from 'lodash'; import _ from 'lodash';
import buildDebug from 'debug'; import buildDebug from 'debug';
import { isObject } from '@verdaccio/utils'; import { isObject } from '@verdaccio/utils';
import { Package, Author } from '@verdaccio/types'; import { Package, Author, ConfigYaml } from '@verdaccio/types';
import { normalizeContributors } from '@verdaccio/store'; import { normalizeContributors } from '@verdaccio/store';
import sanitizyReadme from '@verdaccio/readme'; import sanitizyReadme from '@verdaccio/readme';
@ -109,3 +109,7 @@ export function sortByName(packages: any[], orderAscending: boolean | void = tru
return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1; return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;
}); });
} }
export function hasLogin(config: ConfigYaml) {
return _.isNil(config?.web?.login) || config?.web?.login === true;
}

View File

@ -62,6 +62,19 @@ describe('test web server', () => {
}); });
}); });
test('log in should be disabled', async () => {
return supertest(await initializeServer('login-disabled.yaml'))
.post('/-/verdaccio/login')
.send(
JSON.stringify({
username: 'test',
password: 'test',
})
)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.NOT_FOUND);
});
test.todo('should change password'); test.todo('should change password');
test.todo('should not change password if flag is disabled'); test.todo('should not change password if flag is disabled');
}); });

View File

@ -0,0 +1,33 @@
store:
memory:
limit: 1000
auth:
auth-memory:
users:
test:
name: test
password: test
web:
title: verdaccio
login: false
publish:
allow_offline: false
uplinks:
logs: { type: stdout, format: pretty, level: trace }
packages:
'@*/*':
access: $anonymous
publish: $anonymous
'**':
access: $anonymous
publish: $anonymous
_debug: true
flags:
changePassword: true

View File

@ -1121,7 +1121,7 @@ importers:
debug: 4.3.1 debug: 4.3.1
kleur: ^4.1.3 kleur: ^4.1.3
lodash: ^4.17.20 lodash: ^4.17.20
puppeteer: ^7.1.0 puppeteer: ^9.1.1
request: ^2.88.2 request: ^2.88.2
rimraf: ^3.0.2 rimraf: ^3.0.2
devDependencies: devDependencies:
@ -1130,7 +1130,7 @@ importers:
debug: 4.3.1 debug: 4.3.1
kleur: 4.1.3 kleur: 4.1.3
lodash: 4.17.20 lodash: 4.17.20
puppeteer: 7.1.0 puppeteer: 9.1.1
request: 2.88.2 request: 2.88.2
rimraf: 3.0.2 rimraf: 3.0.2
@ -7676,7 +7676,7 @@ packages:
/@types/yauzl/2.9.1: /@types/yauzl/2.9.1:
resolution: {integrity: sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==} resolution: {integrity: sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==}
dependencies: dependencies:
'@types/node': 14.14.21 '@types/node': 14.14.41
dev: true dev: true
optional: true optional: true
@ -12113,8 +12113,8 @@ packages:
tslib: 1.13.0 tslib: 1.13.0
dev: false dev: false
/devtools-protocol/0.0.847576: /devtools-protocol/0.0.869402:
resolution: {integrity: sha512-0M8kobnSQE0Jmly7Mhbeq0W/PpZfnuK+WjN2ZRVPbGqYwCHCioAVp84H0TcLimgECcN5H976y5QiXMGBC9JKmg==} resolution: {integrity: sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==}
dev: true dev: true
/diacritic/0.0.2: /diacritic/0.0.2:
@ -22243,13 +22243,13 @@ packages:
escape-goat: 2.1.1 escape-goat: 2.1.1
dev: true dev: true
/puppeteer/7.1.0: /puppeteer/9.1.1:
resolution: {integrity: sha512-lqOLzqCKdh7yUAHvK6LxgOpQrL8Bv1/jvS8MLDXxcNms2rlM3E8p/Wlwc7efbRZ0twxTzUeqjN5EqrTwxOwc9g==} resolution: {integrity: sha512-W+nOulP2tYd/ZG99WuZC/I5ljjQQ7EUw/jQGcIb9eu8mDlZxNY2SgcJXTLG9h5gRvqA3uJOe4hZXYsd3EqioMw==}
engines: {node: '>=10.18.1'} engines: {node: '>=10.18.1'}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
debug: 4.3.1 debug: 4.3.1
devtools-protocol: 0.0.847576 devtools-protocol: 0.0.869402
extract-zip: 2.0.1 extract-zip: 2.0.1
https-proxy-agent: 5.0.0 https-proxy-agent: 5.0.0
node-fetch: 2.6.1 node-fetch: 2.6.1
@ -22259,7 +22259,7 @@ packages:
rimraf: 3.0.2 rimraf: 3.0.2
tar-fs: 2.1.0 tar-fs: 2.1.0
unbzip2-stream: 1.4.3 unbzip2-stream: 1.4.3
ws: 7.4.2 ws: 7.4.4
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- supports-color - supports-color
@ -27412,19 +27412,6 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
/ws/7.4.2:
resolution: {integrity: sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==}
engines: {node: '>=8.3.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: true
/ws/7.4.4: /ws/7.4.4:
resolution: {integrity: sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==} resolution: {integrity: sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==}
engines: {node: '>=8.3.0'} engines: {node: '>=8.3.0'}

View File

@ -1,6 +1,7 @@
web: web:
enable: true enable: true
title: verdaccio-server-protected-e2e title: verdaccio-server-protected-e2e
login: true
store: store:
memory: memory:
@ -13,8 +14,7 @@ auth:
name: test name: test
password: test password: test
logs: logs: { type: stdout, format: pretty, level: info }
- { type: stdout, format: pretty, level: info }
packages: packages:
'protected-*': 'protected-*':

View File

@ -1,6 +1,7 @@
web: web:
enable: true enable: true
title: verdaccio-server-e2e title: verdaccio-server-e2e
login: true
store: store:
memory: memory:
@ -13,8 +14,7 @@ auth:
name: test name: test
password: test password: test
logs: logs: { type: stdout, format: pretty, level: info }
- { type: stdout, format: pretty, level: info }
packages: packages:
'@*/*': '@*/*':

View File

@ -40,7 +40,7 @@ describe('/ (Verdaccio Page)', () => {
expect(loginButton).toBeDefined(); expect(loginButton).toBeDefined();
await loginButton.focus(); await loginButton.focus();
await loginButton.click({ delay: 100 }); await loginButton.click({ delay: 100 });
await page.waitFor(500); await page.waitForTimeout(500);
}; };
beforeAll(async () => { beforeAll(async () => {
@ -55,7 +55,7 @@ describe('/ (Verdaccio Page)', () => {
test('should display title', async () => { test('should display title', async () => {
const text = await page.title(); const text = await page.title();
await page.waitFor(1000); await page.waitForTimeout(1000);
expect(text).toContain('verdaccio-server-e2e'); expect(text).toContain('verdaccio-server-e2e');
}); });
@ -87,29 +87,34 @@ describe('/ (Verdaccio Page)', () => {
test('should click on sign in button', async () => { test('should click on sign in button', async () => {
const signInButton = await page.$('button[data-testid="header--button-login"]'); const signInButton = await page.$('button[data-testid="header--button-login"]');
await signInButton.click(); await signInButton.click();
await page.waitFor(1000); await page.waitForTimeout(1000);
const signInDialog = await page.$('#login--dialog'); const signInDialog = await page.$('#login--dialog');
expect(signInDialog).not.toBeNull(); expect(signInDialog).not.toBeNull();
const closeButton = await page.$('button[data-testid="close-login-dialog-button"]'); const closeButton = await page.$('button[data-testid="close-login-dialog-button"]');
await closeButton.click(); await closeButton.click();
await page.waitFor(500); await page.waitForTimeout(500);
}); });
// //
test('should log in an user', async () => { test('should log in an user', async () => {
// we open the dialog // we open the dialog
await logIn(); await logIn();
// verify if logged in
const accountButton = await page.$('#header--button-account');
expect(accountButton).toBeDefined();
// check whether user is logged // check whether user is logged
const buttonLogout = await page.$('#header--button-logout'); const buttonLogout = await page.$('#header--button-logout');
expect(buttonLogout).toBeDefined(); expect(buttonLogout).toBeDefined();
}); });
test('should logout an user', async () => { test('should logout an user', async () => {
// await wa
await page.waitForTimeout(10000);
// we assume the user is logged already // we assume the user is logged already
await clickElement('#header--button-account', { delay: 500 }); await clickElement('#header--button-account', { delay: 500 });
await page.waitFor(1000); await page.waitForTimeout(1000);
await clickElement('#header--button-logout > span', { delay: 500 }); await clickElement('#header--button-logout > span', { delay: 500 });
await page.waitFor(1000); await page.waitForTimeout(1000);
await evaluateSignIn(); await evaluateSignIn();
}); });
// //
@ -117,7 +122,7 @@ describe('/ (Verdaccio Page)', () => {
test('should check registry info dialog', async () => { test('should check registry info dialog', async () => {
const registryInfoButton = await page.$('#header--button-registryInfo'); const registryInfoButton = await page.$('#header--button-registryInfo');
registryInfoButton.click(); registryInfoButton.click();
await page.waitFor(500); await page.waitForTimeout(500);
const registryInfoDialog = await page.$('#registryInfo--dialog-container'); const registryInfoDialog = await page.$('#registryInfo--dialog-container');
expect(registryInfoDialog).not.toBeNull(); expect(registryInfoDialog).not.toBeNull();
@ -129,9 +134,9 @@ describe('/ (Verdaccio Page)', () => {
test('should publish a package', async () => { test('should publish a package', async () => {
await global.__SERVER__.putPackage(scopedPackageMetadata.name, scopedPackageMetadata); await global.__SERVER__.putPackage(scopedPackageMetadata.name, scopedPackageMetadata);
await page.waitFor(1000); await page.waitForTimeout(1000);
await page.reload(); await page.reload();
await page.waitFor(1000); await page.waitForTimeout(1000);
const packagesList = await getPackages(); const packagesList = await getPackages();
expect(packagesList).toHaveLength(1); expect(packagesList).toHaveLength(1);
}); });
@ -142,7 +147,7 @@ describe('/ (Verdaccio Page)', () => {
// console.log("-->packagesList:", packagesList); // console.log("-->packagesList:", packagesList);
const firstPackage = packagesList[0]; const firstPackage = packagesList[0];
await firstPackage.click({ delay: 200 }); await firstPackage.click({ delay: 200 });
await page.waitFor(1000); await page.waitForTimeout(1000);
const readmeText = await page.evaluate( const readmeText = await page.evaluate(
() => document.querySelector('.markdown-body').textContent () => document.querySelector('.markdown-body').textContent
); );
@ -160,7 +165,7 @@ describe('/ (Verdaccio Page)', () => {
const dependenciesTab = await page.$$('#dependencies-tab'); const dependenciesTab = await page.$$('#dependencies-tab');
expect(dependenciesTab).toHaveLength(1); expect(dependenciesTab).toHaveLength(1);
await dependenciesTab[0].click({ delay: 200 }); await dependenciesTab[0].click({ delay: 200 });
await page.waitFor(1000); await page.waitForTimeout(1000);
const tags = await page.$$('.dep-tag'); const tags = await page.$$('.dep-tag');
const tag = tags[0]; const tag = tags[0];
const label = await page.evaluate((el) => el.innerText, tag); const label = await page.evaluate((el) => el.innerText, tag);
@ -171,7 +176,7 @@ describe('/ (Verdaccio Page)', () => {
const versionsTab = await page.$$('#versions-tab'); const versionsTab = await page.$$('#versions-tab');
expect(versionsTab).toHaveLength(1); expect(versionsTab).toHaveLength(1);
await versionsTab[0].click({ delay: 200 }); await versionsTab[0].click({ delay: 200 });
await page.waitFor(1000); await page.waitForTimeout(1000);
const versionItems = await page.$$('.version-item'); const versionItems = await page.$$('.version-item');
expect(versionItems).toHaveLength(2); expect(versionItems).toHaveLength(2);
}); });
@ -180,33 +185,33 @@ describe('/ (Verdaccio Page)', () => {
const upLinksTab = await page.$$('#uplinks-tab'); const upLinksTab = await page.$$('#uplinks-tab');
expect(upLinksTab).toHaveLength(1); expect(upLinksTab).toHaveLength(1);
await upLinksTab[0].click({ delay: 200 }); await upLinksTab[0].click({ delay: 200 });
await page.waitFor(1000); await page.waitForTimeout(1000);
}); });
test('should display readme tab', async () => { test('should display readme tab', async () => {
const readmeTab = await page.$$('#readme-tab'); const readmeTab = await page.$$('#readme-tab');
expect(readmeTab).toHaveLength(1); expect(readmeTab).toHaveLength(1);
await readmeTab[0].click({ delay: 200 }); await readmeTab[0].click({ delay: 200 });
await page.waitFor(1000); await page.waitForTimeout(1000);
}); });
test('should publish a protected package', async () => { test('should publish a protected package', async () => {
await page.goto('http://0.0.0.0:55552'); await page.goto('http://0.0.0.0:55552');
await page.waitFor(500); await page.waitForTimeout(500);
await global.__SERVER_PROTECTED__.putPackage( await global.__SERVER_PROTECTED__.putPackage(
protectedPackageMetadata.name, protectedPackageMetadata.name,
protectedPackageMetadata protectedPackageMetadata
); );
await page.waitFor(500); await page.waitForTimeout(500);
await page.reload(); await page.reload();
await page.waitFor(500); await page.waitForTimeout(500);
const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent); const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent);
expect(text).toMatch('No Package Published Yet'); expect(text).toMatch('No Package Published Yet');
}); });
test('should go to 404 page', async () => { test('should go to 404 page', async () => {
await page.goto('http://0.0.0.0:55552/-/web/detail/@verdaccio/not-found'); await page.goto('http://0.0.0.0:55552/-/web/detail/@verdaccio/not-found');
await page.waitFor(500); await page.waitForTimeout(500);
const text = await page.evaluate(() => document.querySelector('.not-found-text').textContent); const text = await page.evaluate(() => document.querySelector('.not-found-text').textContent);
expect(text).toMatch("Sorry, we couldn't find it..."); expect(text).toMatch("Sorry, we couldn't find it...");
}); });

View File

@ -10,7 +10,7 @@
"request": "^2.88.2", "request": "^2.88.2",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"puppeteer": "^7.1.0" "puppeteer": "^9.1.1"
}, },
"scripts": { "scripts": {
"test": "jest --config jest.config.e2e.js" "test": "jest --config jest.config.e2e.js"