Merge branch 'next' into 14118-update-react-docgen-typescript-plugin

This commit is contained in:
Michael Shilman 2021-05-20 21:07:45 +08:00
commit f2116f29b2
21 changed files with 319 additions and 174 deletions

View File

@ -110,10 +110,11 @@ program
);
program
.command('link <repro-url>')
.description('Pull down a repro from a URL, link it, and run storybook')
.action((reproUrl) =>
link({ reproUrl }).catch((e) => {
.command('link <repo-url-or-directory>')
.description('Pull down a repro from a URL (or a local directory), link it, and run storybook')
.option('--local', 'Link a local directory already in your file system')
.action((target, { local }) =>
link({ target, local }).catch((e) => {
logger.error(e);
process.exit(1);
})

View File

@ -1,14 +1,16 @@
import fse from 'fs-extra';
import path from 'path';
import { sync as spawnSync } from 'cross-spawn';
import { logger } from '@storybook/node-logger';
import { exec } from './repro-generators/scripts';
interface LinkOptions {
reproUrl: string;
target: string;
local?: boolean;
}
export const link = async ({ reproUrl }: LinkOptions) => {
const storybookDirectory = process.cwd();
export const link = async ({ target, local }: LinkOptions) => {
const storybookDir = process.cwd();
try {
const packageJson = JSON.parse(fse.readFileSync('package.json', 'utf8'));
if (packageJson.name !== '@storybook/root') throw new Error();
@ -16,28 +18,46 @@ export const link = async ({ reproUrl }: LinkOptions) => {
throw new Error('Expected to run link from the root of the storybook monorepo');
}
const reprosDirectory = path.join(storybookDirectory, '../storybook-repros');
logger.info(`Ensuring directory ${reprosDirectory}`);
fse.ensureDirSync(reprosDirectory);
let reproDir = target;
let reproName = path.basename(target);
logger.info(`Cloning ${reproUrl}`);
await exec(`git clone ${reproUrl}`, { cwd: reprosDirectory });
// Extract a repro name from url given as input (take the last part of the path and remove the extension)
const reproName = path.basename(reproUrl, path.extname(reproUrl));
const repro = path.join(reprosDirectory, reproName);
if (!local) {
const reprosDir = path.join(storybookDir, '../storybook-repros');
logger.info(`Ensuring directory ${reprosDir}`);
fse.ensureDirSync(reprosDir);
logger.info(`Linking ${repro}`);
await exec(`yarn link --all ${storybookDirectory}`, { cwd: repro });
logger.info(`Cloning ${target}`);
await exec(`git clone ${target}`, { cwd: reprosDir });
// Extract a repro name from url given as input (take the last part of the path and remove the extension)
reproName = path.basename(target, path.extname(target));
reproDir = path.join(reprosDir, reproName);
}
const version = spawnSync('yarn', ['--version'], {
cwd: reproDir,
stdio: 'pipe',
}).stdout.toString();
if (!version.startsWith('2.')) {
logger.warn(`🚨 Expected yarn 2 in ${reproDir}!`);
logger.warn('');
logger.warn('Please set it up with `yarn set version berry`,');
logger.warn(`then link '${reproDir}' with the '--local' flag.`);
return;
}
logger.info(`Linking ${reproDir}`);
await exec(`yarn link --all ${storybookDir}`, { cwd: reproDir });
logger.info(`Installing ${reproName}`);
await exec(`yarn install`, { cwd: repro });
await exec(`yarn install`, { cwd: reproDir });
// ⚠️ TODO: Fix peer deps in `@storybook/preset-create-react-app`
logger.info(
`Magic stuff related to @storybook/preset-create-react-app, we need to fix peerDependencies`
);
await exec(`yarn add -D webpack-hot-middleware`, { cwd: repro });
await exec(`yarn add -D webpack-hot-middleware`, { cwd: reproDir });
logger.info(`Running ${reproName} storybook`);
await exec(`yarn run storybook`, { cwd: repro });
await exec(`yarn run storybook`, { cwd: reproDir });
};

View File

@ -2,6 +2,7 @@ import React, { FC, ChangeEvent, useCallback, useState } from 'react';
import { styled } from '@storybook/theming';
import { Form } from '../form';
import { getControlId } from './helpers';
import { ControlProps, ArrayValue, ArrayConfig } from './types';
const parse = (value: string, separator: string): ArrayValue =>
@ -45,7 +46,7 @@ export const ArrayControl: FC<ArrayProps> = ({
return (
<Wrapper>
<Form.Textarea
id={name}
id={getControlId(name)}
value={format(value, separator)}
onChange={handleChange}
size="flex"

View File

@ -3,6 +3,7 @@ import React, { FC, useCallback } from 'react';
import { opacify, transparentize } from 'polished';
import { styled } from '@storybook/theming';
import { getControlId } from './helpers';
import { Form } from '../form';
import { ControlProps, BooleanValue, BooleanConfig } from './types';
@ -90,7 +91,7 @@ export const BooleanControl: FC<BooleanProps> = ({ name, value, onChange, onBlur
return (
<Label htmlFor={name} title={value ? 'Change to false' : 'Change to true'}>
<input
id={name}
id={getControlId(name)}
type="checkbox"
onChange={(e) => onChange(e.target.checked)}
checked={value || false}

View File

@ -9,6 +9,7 @@ import { TooltipNote } from '../tooltip/TooltipNote';
import { WithTooltip } from '../tooltip/lazy-WithTooltip';
import { Form } from '../form';
import { Icons } from '../icon/icon';
import { getControlId } from './helpers';
const Wrapper = styled.div({
position: 'relative',
@ -274,6 +275,7 @@ const usePresets = (
export type ColorProps = ControlProps<ColorValue> & ColorConfig;
export const ColorControl: FC<ColorProps> = ({
name,
value: initialValue,
onChange,
onFocus,
@ -325,6 +327,7 @@ export const ColorControl: FC<ColorProps> = ({
<Swatch value={realValue} style={{ margin: 4 }} />
</PickerTooltip>
<Input
id={getControlId(name)}
value={value}
onChange={(e: any) => updateValue(e.target.value)}
onFocus={(e) => e.target.select()}

View File

@ -3,6 +3,7 @@ import { styled } from '@storybook/theming';
import { Form } from '../form';
import { ControlProps, DateValue, DateConfig } from './types';
import { getControlId } from './helpers';
const parseDate = (value: string) => {
const [year, month, day] = value.split('-');
@ -89,21 +90,23 @@ export const DateControl: FC<DateProps> = ({ name, value, onChange, onFocus, onB
setValid(!!time);
};
const controlId = getControlId(name);
return (
<FlexSpaced>
<Form.Input
type="date"
max="9999-12-31" // I do this because of a rendering bug in chrome
ref={dateRef as RefObject<HTMLInputElement>}
id={`${name}date`}
name={`${name}date`}
id={`${controlId}-date`}
name={`${controlId}-date`}
onChange={onDateChange}
{...{ onFocus, onBlur }}
/>
<Form.Input
type="time"
id={`${name}time`}
name={`${name}time`}
id={`${controlId}-time`}
name={`${controlId}-time`}
ref={timeRef as RefObject<HTMLInputElement>}
onChange={onTimeChange}
{...{ onFocus, onBlur }}

View File

@ -3,6 +3,7 @@ import { styled } from '@storybook/theming';
import { ControlProps } from './types';
import { Form } from '../form';
import { getControlId } from './helpers';
export interface FilesControlProps extends ControlProps<string[]> {
accept?: string;
@ -37,6 +38,7 @@ export const FilesControl: FunctionComponent<FilesControlProps> = ({
return (
<FileInput
id={getControlId(name)}
type="file"
name={name}
multiple

View File

@ -2,6 +2,7 @@ import React, { FC, ChangeEvent, useState, useCallback, useEffect, useRef } from
import { styled } from '@storybook/theming';
import { Form } from '../form';
import { getControlId } from './helpers';
import { ControlProps, NumberValue, NumberConfig } from './types';
const Wrapper = styled.label({
@ -65,6 +66,7 @@ export const NumberControl: FC<NumberProps> = ({
<Wrapper>
<Form.Input
ref={htmlElRef}
id={getControlId(name)}
type="number"
onChange={handleChange}
size="flex"

View File

@ -13,6 +13,7 @@ import { styled, useTheme, Theme } from '@storybook/theming';
// @ts-ignore
import { JsonTree, getObjectType } from './react-editable-json-tree';
import { getControlId } from './helpers';
import type { ControlProps, ObjectValue, ObjectConfig } from './types';
import { Form } from '../form';
import { Icons, IconsProps } from '../icon/icon';
@ -274,7 +275,7 @@ export const ObjectControl: React.FC<ObjectProps> = ({ name, value, onChange })
const rawJSONForm = (
<RawInput
ref={htmlElRef}
id={name}
id={getControlId(name)}
name={name}
defaultValue={value === null ? '' : JSON.stringify(value, null, 2)}
onBlur={(event) => updateRaw(event.target.value)}

View File

@ -2,6 +2,7 @@ import React, { FC, ChangeEvent } from 'react';
import { styled } from '@storybook/theming';
import { lighten, darken, rgba } from 'polished';
import { getControlId } from './helpers';
import { ControlProps, NumberValue, RangeConfig } from './types';
import { parse } from './Number';
@ -169,6 +170,7 @@ export const RangeControl: FC<RangeProps> = ({
<RangeWrapper>
<RangeLabel>{min}</RangeLabel>
<RangeInput
id={getControlId(name)}
type="range"
onChange={handleChange}
{...{ name, value, min, max, step, onFocus, onBlur }}

View File

@ -2,6 +2,7 @@ import React, { FC, ChangeEvent, useCallback, useState } from 'react';
import { styled } from '@storybook/theming';
import { Form } from '../form';
import { getControlId } from './helpers';
import { ControlProps, TextValue, TextConfig } from './types';
export type TextProps = ControlProps<TextValue | undefined> & TextConfig;
@ -14,7 +15,7 @@ export const TextControl: FC<TextProps> = ({ name, value, onChange, onFocus, onB
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
onChange(event.target.value);
};
const [forceVisible, setForceVisible] = useState(false);
const onForceVisible = useCallback(() => {
onChange('');
@ -28,7 +29,7 @@ export const TextControl: FC<TextProps> = ({ name, value, onChange, onFocus, onB
return (
<Wrapper>
<Form.Textarea
id={name}
id={getControlId(name)}
onChange={handleChange}
size="flex"
placeholder="Edit string..."

View File

@ -0,0 +1,12 @@
import { getControlId } from './helpers';
describe('getControlId', () => {
it.each([
// caseName, input, expected
['lower case', 'some-id', 'control-some-id'],
['upper case', 'SOME-ID', 'control-SOME-ID'],
['all valid characters', 'some_weird-:custom.id', 'control-some_weird-:custom.id'],
])('%s', (a, input, expected) => {
expect(getControlId(input)).toBe(expected);
});
});

View File

@ -0,0 +1,7 @@
/**
* Adds `control` prefix to make ID attribute more specific.
* Removes spaces because spaces are not allowed in ID attributes
* @link http://xahlee.info/js/html_allowed_chars_in_attribute.html
* @example getControlId('my prop name') -> 'control-my-prop-name'
*/
export const getControlId = (value: string) => `control-${value.replace(/\s+/g, '-')}`;

View File

@ -3,6 +3,7 @@ import { styled } from '@storybook/theming';
import { logger } from '@storybook/client-logger';
import { ControlProps, OptionsMultiSelection, NormalizedOptionsConfig } from '../types';
import { selectedKeys, selectedValues } from './helpers';
import { getControlId } from '../helpers';
const Wrapper = styled.div<{ isInline: boolean }>(({ isInline }) =>
isInline
@ -69,10 +70,12 @@ export const CheckboxControl: FC<CheckboxProps> = ({
setSelected(updated);
};
const controlId = getControlId(name);
return (
<Wrapper isInline={isInline}>
{Object.keys(options).map((key) => {
const id = `${name}-${key}`;
{Object.keys(options).map((key, index) => {
const id = `${controlId}-${index}`;
return (
<Label key={id} htmlFor={id}>
<input

View File

@ -3,6 +3,7 @@ import { styled } from '@storybook/theming';
import { logger } from '@storybook/client-logger';
import { ControlProps, OptionsSingleSelection, NormalizedOptionsConfig } from '../types';
import { selectedKey } from './helpers';
import { getControlId } from '../helpers';
const Wrapper = styled.div<{ isInline: boolean }>(({ isInline }) =>
isInline
@ -54,10 +55,12 @@ export const RadioControl: FC<RadioProps> = ({ name, options, value, onChange, i
return <>-</>;
}
const selection = selectedKey(value, options);
const controlId = getControlId(name);
return (
<Wrapper isInline={isInline}>
{Object.keys(options).map((key) => {
const id = `${name}-${key}`;
{Object.keys(options).map((key, index) => {
const id = `${controlId}-${index}`;
return (
<Label key={id} htmlFor={id}>
<input

View File

@ -4,6 +4,7 @@ import { logger } from '@storybook/client-logger';
import { ControlProps, OptionsSelection, NormalizedOptionsConfig } from '../types';
import { selectedKey, selectedKeys, selectedValues } from './helpers';
import { Icons } from '../../icon/icon';
import { getControlId } from '../helpers';
const styleResets: CSSObject = {
// resets
@ -94,11 +95,12 @@ const SingleSelect: FC<SelectProps> = ({ name, value, options, onChange }) => {
onChange(options[e.currentTarget.value]);
};
const selection = selectedKey(value, options) || NO_SELECTION;
const controlId = getControlId(name);
return (
<SelectWrapper>
<Icons icon="arrowdown" />
<OptionsSelect value={selection} onChange={handleChange}>
<OptionsSelect id={controlId} value={selection} onChange={handleChange}>
<option key="no-selection" disabled>
{NO_SELECTION}
</option>
@ -118,10 +120,11 @@ const MultiSelect: FC<SelectProps> = ({ name, value, options, onChange }) => {
onChange(selectedValues(selection, options));
};
const selection = selectedKeys(value, options);
const controlId = getControlId(name);
return (
<SelectWrapper>
<OptionsSelect multiple value={selection} onChange={handleChange}>
<OptionsSelect id={controlId} multiple value={selection} onChange={handleChange}>
{Object.keys(options).map((key) => (
<option key={key}>{key}</option>
))}

View File

@ -186,7 +186,7 @@
"@types/lodash": "^4.14.167",
"@types/node": "^14.14.20",
"@types/node-cleanup": "^2.1.1",
"@types/prompts": "^2.0.9",
"@types/prompts": "2.0.11",
"@types/semver": "^7.3.4",
"@types/serve-static": "^1.13.8",
"@types/shelljs": "^0.8.7",
@ -211,7 +211,6 @@
"danger": "^10.6.2",
"detect-port": "^1.3.0",
"downlevel-dts": "^0.6.0",
"enquirer": "^2.3.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"eslint": "^7.17.0",

View File

@ -1,17 +1,15 @@
/* eslint-disable no-irregular-whitespace */
import path from 'path';
import { remove, ensureDir, pathExists } from 'fs-extra';
import { prompt } from 'enquirer';
import pLimit from 'p-limit';
import { ensureDir, pathExists, remove } from 'fs-extra';
import prompts from 'prompts';
import program from 'commander';
import { serve } from './utils/serve';
import { exec } from './utils/command';
// @ts-ignore
import { filterDataForCurrentCircleCINode } from './utils/concurrency';
import * as configs from '../lib/cli/src/repro-generators/configs';
import { Parameters } from '../lib/cli/src/repro-generators/configs';
import { exec } from '../lib/cli/src/repro-generators/scripts';
const logger = console;
@ -33,13 +31,7 @@ const prepareDirectory = async ({ cwd }: Options): Promise<boolean> => {
await ensureDir(siblingDir);
}
const cwdExists = await pathExists(cwd);
if (cwdExists) {
return true;
}
return false;
return pathExists(cwd);
};
const cleanDirectory = async ({ cwd }: Options): Promise<void> => {
@ -47,37 +39,30 @@ const cleanDirectory = async ({ cwd }: Options): Promise<void> => {
};
const buildStorybook = async ({ cwd }: Options) => {
logger.info(`👷Building Storybook`);
try {
await exec(`yarn build-storybook --quiet`, { cwd });
} catch (e) {
logger.error(`🚨Storybook build failed`);
throw e;
}
await exec(
`yarn build-storybook --quiet`,
{ cwd, silent: false },
{ startMessage: `👷 Building Storybook`, errorMessage: `🚨 Storybook build failed` }
);
};
const serveStorybook = async ({ cwd }: Options, port: string) => {
const staticDirectory = path.join(cwd, 'storybook-static');
logger.info(`🌍Serving ${staticDirectory} on http://localhost:${port}`);
logger.info(`🌍 Serving ${staticDirectory} on http://localhost:${port}`);
return serve(staticDirectory, port);
};
const runCypress = async ({ name }: Options, location: string, open: boolean) => {
const cypressCommand = open ? 'open' : 'run';
logger.info(`🤖Running Cypress tests`);
try {
await exec(
`yarn cypress ${cypressCommand} --config pageLoadTimeout=4000,execTimeout=4000,taskTimeout=4000,responseTimeout=4000,integrationFolder="cypress/generated" --env location="${location}"`,
{ cwd: rootDir }
);
logger.info(`E2E tests success`);
logger.info(`🎉Storybook is working great with ${name}!`);
} catch (e) {
logger.error(`🚨E2E tests fails`);
logger.info(`🥺Storybook has some issues with ${name}!`);
throw e;
}
const runCypress = async (location: string) => {
const cypressCommand = openCypressInUIMode ? 'open' : 'run';
await exec(
`yarn cypress ${cypressCommand} --config pageLoadTimeout=4000,execTimeout=4000,taskTimeout=4000,responseTimeout=4000,integrationFolder="cypress/generated" --env location="${location}"`,
{ cwd: rootDir },
{
startMessage: `🤖 Running Cypress tests`,
errorMessage: `🚨 E2E tests fails`,
}
);
};
const runTests = async ({ name, ...rest }: Parameters) => {
@ -88,7 +73,7 @@ const runTests = async ({ name, ...rest }: Parameters) => {
};
logger.log();
logger.info(`🏃‍♀Starting for ${name}`);
logger.info(`🏃Starting for ${name}`);
logger.log();
logger.debug(options);
logger.log();
@ -113,8 +98,14 @@ const runTests = async ({ name, ...rest }: Parameters) => {
}
const command = `${sbCLICommand} ${commandArgs.join(' ')}`;
logger.debug(command);
await exec(command, { cwd: siblingDir });
await exec(
command,
{ cwd: siblingDir, silent: false },
{
startMessage: `👷 Bootstrapping ${options.framework} project`,
errorMessage: `🚨 Unable to bootstrap project`,
}
);
await buildStorybook(options);
logger.log();
@ -123,120 +114,202 @@ const runTests = async ({ name, ...rest }: Parameters) => {
const server = await serveStorybook(options, '4000');
logger.log();
let open = false;
if (!process.env.CI) {
({ open } = await prompt({
type: 'confirm',
name: 'open',
message: 'Should open cypress?',
}));
}
try {
await runCypress(options, 'http://localhost:4000', open);
logger.log();
await runCypress('http://localhost:4000');
logger.info(`🎉 Storybook is working great with ${name}!`);
} catch (e) {
logger.info(`🥺 Storybook has some issues with ${name}!`);
} finally {
server.close();
}
};
// Run tests!
const runE2E = async (parameters: Parameters) => {
const { name } = parameters;
const cwd = path.join(siblingDir, `${name}`);
async function postE2ECleanup(cwd: string, parameters: Parameters) {
if (!process.env.CI) {
const { cleanup } = await prompts({
type: 'toggle',
name: 'cleanup',
message: 'Should perform cleanup?',
initial: false,
active: 'yes',
inactive: 'no',
});
if (cleanup) {
logger.log();
logger.info(`🗑 Cleaning ${cwd}`);
await cleanDirectory({ ...parameters, cwd });
} else {
logger.log();
logger.info(`🚯 No cleanup happened: ${cwd}`);
}
}
}
async function preE2ECleanup(name: string, parameters: Parameters, cwd: string) {
if (startWithCleanSlate) {
logger.log();
logger.info(`♻️  Starting with a clean slate, removing existing ${name} folder`);
logger.info(`♻️ Starting with a clean slate, removing existing ${name} folder`);
await cleanDirectory({ ...parameters, cwd });
}
}
return runTests(parameters)
.then(async () => {
if (!process.env.CI) {
const { cleanup } = await prompt<{ cleanup: boolean }>({
type: 'confirm',
name: 'cleanup',
message: 'Should perform cleanup?',
});
if (cleanup) {
logger.log();
logger.info(`🗑Cleaning ${cwd}`);
await cleanDirectory({ ...parameters, cwd });
} else {
logger.log();
logger.info(`🚯No cleanup happened: ${cwd}`);
}
}
})
/**
* Execute E2E for input parameters and return true is everything is ok, false
* otherwise.
* @param parameters
*/
const runE2E = async (parameters: Parameters): Promise<boolean> => {
const { name } = parameters;
const cwd = path.join(siblingDir, `${name}`);
return preE2ECleanup(name, parameters, cwd)
.then(() => runTests(parameters))
.then(() => postE2ECleanup(cwd, parameters))
.then(() => true)
.catch((e) => {
logger.error(`🛑an error occurred:\n${e}`);
logger.log();
logger.error(`🛑 an error occurred:`);
logger.error(e);
logger.log();
process.exitCode = 1;
return false;
});
};
program.option('--clean', 'Clean up existing projects before running the tests', false);
program.option('--pnp', 'Run tests using Yarn 2 PnP instead of Yarn 1 + npx', false);
program.option(
'--use-local-sb-cli',
'Run tests using local @storybook/cli package (⚠️ Be sure @storybook/cli is properly build as it will not be rebuild before running the tests)',
false
);
program.option(
'--skip <value>',
'Skip a framework, can be used multiple times "--skip angular@latest --skip preact"',
(value, previous) => previous.concat([value]),
[]
);
program
.option('--clean', 'Clean up existing projects before running the tests', false)
.option('--pnp', 'Run tests using Yarn 2 PnP instead of Yarn 1 + npx', false)
.option(
'--use-local-sb-cli',
'Run tests using local @storybook/cli package (⚠️ Be sure @storybook/cli is properly built as it will not be rebuilt before running the tests)',
false
)
.option(
'--skip <value>',
'Skip a framework, can be used multiple times "--skip angular@latest --skip preact"',
(value, previous) => previous.concat([value]),
[]
)
.option('--all', `run e2e tests for every framework`, false);
program.parse(process.argv);
const {
pnp,
useLocalSbCli,
clean: startWithCleanSlate,
args: frameworkArgs,
skip: frameworksToSkip,
}: {
type ProgramOptions = {
all?: boolean;
pnp?: boolean;
useLocalSbCli?: boolean;
clean?: boolean;
args?: string[];
skip?: string[];
} = program;
};
const {
all: shouldRunAllFrameworks,
args: frameworkArgs,
skip: frameworksToSkip,
}: ProgramOptions = program;
let { pnp, useLocalSbCli, clean: startWithCleanSlate }: ProgramOptions = program;
const typedConfigs: { [key: string]: Parameters } = configs;
const e2eConfigs: { [key: string]: Parameters } = {};
let e2eConfigs: { [key: string]: Parameters } = {};
// Compute the list of frameworks we will run E2E for
if (frameworkArgs.length > 0) {
frameworkArgs.forEach((framework) => {
e2eConfigs[framework] = Object.values(typedConfigs).find((c) => c.name === framework);
let openCypressInUIMode = !process.env.CI;
const getConfig = async () => {
if (shouldRunAllFrameworks) {
logger.info(`📑 Running test for ALL frameworks`);
Object.values(typedConfigs).forEach((config) => {
e2eConfigs[`${config.name}-${config.version}`] = config;
});
}
// Compute the list of frameworks we will run E2E for
if (frameworkArgs.length > 0) {
frameworkArgs.forEach((framework) => {
e2eConfigs[framework] = Object.values(typedConfigs).find((c) => c.name === framework);
});
} else {
const selectedValues = await prompts([
{
type: 'toggle',
name: 'openCypressInUIMode',
message: 'Open cypress in UI mode',
initial: false,
active: 'yes',
inactive: 'no',
},
{
type: 'toggle',
name: 'useLocalSbCli',
message: 'Use local Storybook CLI',
initial: false,
active: 'yes',
inactive: 'no',
},
{
type: 'autocompleteMultiselect',
message: 'Select the frameworks to run',
name: 'frameworks',
hint:
'You can also run directly with package name like `test:e2e-framework react`, or `yarn test:e2e-framework --all` for all packages!',
choices: Object.keys(configs).map((key) => {
// @ts-ignore
const { name, version } = configs[key];
return {
// @ts-ignore
value: configs[key],
title: `${name}@${version}`,
selected: false,
};
}),
},
]);
if (!selectedValues.frameworks) {
logger.info(`No framework was selected.`);
process.exit(process.exitCode || 0);
}
useLocalSbCli = selectedValues.useLocalSbCli;
openCypressInUIMode = selectedValues.openCypressInUIMode;
e2eConfigs = selectedValues.frameworks;
}
// Remove frameworks listed with `--skip` arg
frameworksToSkip.forEach((framework) => {
delete e2eConfigs[framework];
});
} else {
Object.values(typedConfigs).forEach((config) => {
e2eConfigs[config.name] = config;
});
}
};
// Remove frameworks listed with `--skip` arg
frameworksToSkip.forEach((framework) => {
delete e2eConfigs[framework];
});
const perform = () => {
const limit = pLimit(1);
const perform = async (): Promise<Record<string, boolean>> => {
await getConfig();
const narrowedConfigs = Object.values(e2eConfigs);
const list = filterDataForCurrentCircleCINode(narrowedConfigs) as Parameters[];
logger.info(`📑 Will run E2E tests for:${list.map((c) => `${c.name}`).join(', ')}`);
return Promise.all(list.map((config) => limit(() => runE2E(config))));
const e2eResult: Record<string, boolean> = {};
// Run all e2e tests one after another and fill result map
await list.reduce(
(previousValue, config) =>
previousValue
.then(() => runE2E(config))
.then((result) => {
e2eResult[config.name] = result;
}),
Promise.resolve()
);
return e2eResult;
};
perform().then(() => {
perform().then((e2eResult) => {
logger.info(`🧮 E2E Results`);
Object.entries(e2eResult).forEach(([configName, result]) => {
logger.info(`${configName}: ${result ? 'OK' : 'KO'}`);
});
process.exit(process.exitCode || 0);
});

View File

@ -1,19 +1,19 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["node", "jest"],
"strict": false,
"strictNullChecks": false,
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"paths": {
"verdaccio": ["./typings.d.ts"]
}
},
"include": ["./**/*"]
}
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["node", "jest"],
"strict": false,
"strictNullChecks": false,
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"paths": {
"verdaccio": ["./typings.d.ts"]
}
},
"include": ["./**/*"]
}

View File

@ -1 +1 @@
declare module 'verdaccio';
declare module 'verdaccio';

View File

@ -7206,7 +7206,7 @@ __metadata:
"@types/lodash": ^4.14.167
"@types/node": ^14.14.20
"@types/node-cleanup": ^2.1.1
"@types/prompts": ^2.0.9
"@types/prompts": 2.0.11
"@types/semver": ^7.3.4
"@types/serve-static": ^1.13.8
"@types/shelljs": ^0.8.7
@ -7232,7 +7232,6 @@ __metadata:
danger: ^10.6.2
detect-port: ^1.3.0
downlevel-dts: ^0.6.0
enquirer: ^2.3.6
enzyme: ^3.11.0
enzyme-adapter-react-16: ^1.15.5
eslint: ^7.17.0
@ -8705,6 +8704,15 @@ __metadata:
languageName: node
linkType: hard
"@types/prompts@npm:2.0.11":
version: 2.0.11
resolution: "@types/prompts@npm:2.0.11"
dependencies:
"@types/node": "*"
checksum: f0d1111156a4ea4ec80dac1f0354f53b94f79e2627ff092adc18218a4919f946f82e191cb3f58a2fa8fbc73d02fcaf5c07c633b59469f87d3f8cd334608669a1
languageName: node
linkType: hard
"@types/prompts@npm:^2.0.9":
version: 2.4.0
resolution: "@types/prompts@npm:2.4.0"