mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-23 14:39:32 +08:00
commit
edc3d288c4
46
.pnp.js
generated
46
.pnp.js
generated
@ -3833,6 +3833,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["jest", "npm:26.6.3"],
|
||||
["js-yaml", "npm:3.14.0"],
|
||||
["opener", "npm:1.5.2"],
|
||||
["ora", "npm:5.1.0"],
|
||||
["react", "npm:17.0.1"],
|
||||
["react-dom", "virtual:22157ea722f8d6428f1fcf0a6f7f6c7d6b902d9c785256c60a65fe6cd0db76ebccc7c1457ee047df0ba6909ff018e300c4f4957a60f5b670089810dfc417af9b#npm:17.0.1"],
|
||||
["reload", "npm:3.1.1"],
|
||||
@ -9012,6 +9013,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["cli-spinners", [
|
||||
["npm:2.5.0", {
|
||||
"packageLocation": "./.yarn/cache/cli-spinners-npm-2.5.0-31add52b01-a275c35881.zip/node_modules/cli-spinners/",
|
||||
"packageDependencies": [
|
||||
["cli-spinners", "npm:2.5.0"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["cli-width", [
|
||||
["npm:2.2.1", {
|
||||
"packageLocation": "./.yarn/cache/cli-width-npm-2.2.1-4bdb77393c-f7c830bddc.zip/node_modules/cli-width/",
|
||||
@ -14835,6 +14845,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["is-interactive", [
|
||||
["npm:1.0.0", {
|
||||
"packageLocation": "./.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-d79b435e51.zip/node_modules/is-interactive/",
|
||||
"packageDependencies": [
|
||||
["is-interactive", "npm:1.0.0"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["is-natural-number", [
|
||||
["npm:4.0.1", {
|
||||
"packageLocation": "./.yarn/cache/is-natural-number-npm-4.0.1-b5fd86a31d-8b0f8a5f5c.zip/node_modules/is-natural-number/",
|
||||
@ -16522,6 +16541,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["log-symbols", [
|
||||
["npm:4.0.0", {
|
||||
"packageLocation": "./.yarn/cache/log-symbols-npm-4.0.0-7291c4d053-2cbdb0427d.zip/node_modules/log-symbols/",
|
||||
"packageDependencies": [
|
||||
["log-symbols", "npm:4.0.0"],
|
||||
["chalk", "npm:4.1.0"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["loglevel", [
|
||||
["npm:1.7.1", {
|
||||
"packageLocation": "./.yarn/cache/loglevel-npm-1.7.1-46e39bd115-abee97e346.zip/node_modules/loglevel/",
|
||||
@ -18451,6 +18480,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["ora", [
|
||||
["npm:5.1.0", {
|
||||
"packageLocation": "./.yarn/cache/ora-npm-5.1.0-0f7ce18b2d-53aad8d299.zip/node_modules/ora/",
|
||||
"packageDependencies": [
|
||||
["ora", "npm:5.1.0"],
|
||||
["chalk", "npm:4.1.0"],
|
||||
["cli-cursor", "npm:3.1.0"],
|
||||
["cli-spinners", "npm:2.5.0"],
|
||||
["is-interactive", "npm:1.0.0"],
|
||||
["log-symbols", "npm:4.0.0"],
|
||||
["mute-stream", "npm:0.0.8"],
|
||||
["strip-ansi", "npm:6.0.0"],
|
||||
["wcwidth", "npm:1.0.1"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["original", [
|
||||
["npm:1.0.2", {
|
||||
"packageLocation": "./.yarn/cache/original-npm-1.0.2-2250635ba0-6918b9d454.zip/node_modules/original/",
|
||||
|
BIN
.yarn/cache/cli-spinners-npm-2.5.0-31add52b01-a275c35881.zip
vendored
Normal file
BIN
.yarn/cache/cli-spinners-npm-2.5.0-31add52b01-a275c35881.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-d79b435e51.zip
vendored
Normal file
BIN
.yarn/cache/is-interactive-npm-1.0.0-7ff7c6e04a-d79b435e51.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/log-symbols-npm-4.0.0-7291c4d053-2cbdb0427d.zip
vendored
Normal file
BIN
.yarn/cache/log-symbols-npm-4.0.0-7291c4d053-2cbdb0427d.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/ora-npm-5.1.0-0f7ce18b2d-53aad8d299.zip
vendored
Normal file
BIN
.yarn/cache/ora-npm-5.1.0-0f7ce18b2d-53aad8d299.zip
vendored
Normal file
Binary file not shown.
@ -100,7 +100,6 @@ class RefBuilder {
|
||||
constructor({ context }) {
|
||||
this.rootPath = 'lowdefy.yaml';
|
||||
this.configLoader = context.configLoader;
|
||||
this.logger = context.logger;
|
||||
this.refContent = {};
|
||||
this.MAX_RECURSION_DEPTH = context.MAX_RECURSION_DEPTH || 20;
|
||||
}
|
||||
|
@ -23,8 +23,6 @@ async function testSchema({ components, context }) {
|
||||
await context.logger.warn('Schema not valid.');
|
||||
const promises = errors.map((err) => context.logger.warn(formatErrorMessage(err, components)));
|
||||
await promises;
|
||||
} else {
|
||||
await context.logger.log('Schema valid.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,24 +18,21 @@ import testSchema from './testSchema';
|
||||
import testContext from '../test/testContext';
|
||||
|
||||
const mockLogWarn = jest.fn();
|
||||
const mockLog = jest.fn();
|
||||
|
||||
const logger = {
|
||||
warn: mockLogWarn,
|
||||
log: mockLog,
|
||||
};
|
||||
|
||||
const context = testContext({ logger });
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogWarn.mockReset();
|
||||
mockLog.mockReset();
|
||||
});
|
||||
|
||||
test('empty components', async () => {
|
||||
const components = {};
|
||||
await testSchema({ components, context });
|
||||
expect(mockLog.mock.calls).toEqual([['Schema valid.']]);
|
||||
expect().toBe();
|
||||
});
|
||||
|
||||
test('app schema', async () => {
|
||||
@ -70,7 +67,7 @@ test('app schema', async () => {
|
||||
],
|
||||
};
|
||||
testSchema({ components, context });
|
||||
expect(mockLog.mock.calls).toEqual([['Schema valid.']]);
|
||||
expect().toBe();
|
||||
});
|
||||
|
||||
test('invalid schema', async () => {
|
||||
|
@ -20,6 +20,7 @@ function testContext({ artifactSetter, configLoader, logger = {}, metaLoader } =
|
||||
log: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
succeed: () => {},
|
||||
};
|
||||
|
||||
const context = {
|
||||
|
@ -15,16 +15,26 @@
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { type } from '@lowdefy/helpers';
|
||||
import { type as typeHelper } from '@lowdefy/helpers';
|
||||
|
||||
async function fetchMetaUrl(location) {
|
||||
if (type.isNone(location)) {
|
||||
async function fetchMetaUrl({ location, type } = {}) {
|
||||
if (typeHelper.isNone(location)) {
|
||||
throw new Error('Failed to fetch meta, location is undefined.');
|
||||
}
|
||||
if (!type.isString(location.url)) {
|
||||
throw new Error('Location url definition should be a string.');
|
||||
if (!typeHelper.isString(location.url)) {
|
||||
throw new Error(`Block type ${JSON.stringify(type)} url definition should be a string.`);
|
||||
}
|
||||
let res;
|
||||
try {
|
||||
res = await axios.get(location.url);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 404) {
|
||||
throw new Error(
|
||||
`Meta for type ${JSON.stringify(type)} could not be found at ${JSON.stringify(location)}.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const res = await axios.get(location.url);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,20 @@
|
||||
import axios from 'axios';
|
||||
import fetchMetaUrl from './fetchMetaUrl';
|
||||
|
||||
const type = 'Type';
|
||||
|
||||
jest.mock('axios', () => {
|
||||
return {
|
||||
get: (url) => {
|
||||
if (url === 'valid-url') {
|
||||
return Promise.resolve({ data: { key: 'value' } });
|
||||
}
|
||||
if (url === '404') {
|
||||
const error = new Error('Test 404');
|
||||
error.response = {};
|
||||
error.response.status = 404;
|
||||
throw error;
|
||||
}
|
||||
throw new Error('Invalid url');
|
||||
},
|
||||
};
|
||||
@ -31,7 +39,10 @@ jest.mock('axios', () => {
|
||||
|
||||
test('fetchMetaUrl fetches from url', async () => {
|
||||
const meta = await fetchMetaUrl({
|
||||
url: 'valid-url',
|
||||
type,
|
||||
location: {
|
||||
url: 'valid-url',
|
||||
},
|
||||
});
|
||||
expect(meta).toEqual({ key: 'value' });
|
||||
});
|
||||
@ -39,17 +50,42 @@ test('fetchMetaUrl fetches from url', async () => {
|
||||
test('fetchMetaUrl request errors', async () => {
|
||||
await expect(
|
||||
fetchMetaUrl({
|
||||
url: 'invalid-url',
|
||||
type,
|
||||
location: {
|
||||
url: 'invalid-url',
|
||||
},
|
||||
})
|
||||
).rejects.toThrow('Invalid url');
|
||||
});
|
||||
|
||||
test('fetchMetaUrl throws if location is undefined', async () => {
|
||||
test('fetchMetaUrl throws if args are undefined', async () => {
|
||||
await expect(fetchMetaUrl()).rejects.toThrow('Failed to fetch meta, location is undefined.');
|
||||
});
|
||||
|
||||
test('fetchMetaUrl throws if location is undefined', async () => {
|
||||
await expect(fetchMetaUrl({ url: 1 })).rejects.toThrow(
|
||||
'Location url definition should be a string.'
|
||||
await expect(fetchMetaUrl({ type })).rejects.toThrow(
|
||||
'Failed to fetch meta, location is undefined.'
|
||||
);
|
||||
});
|
||||
|
||||
test('fetchMetaUrl throws if location is not a string', async () => {
|
||||
await expect(
|
||||
fetchMetaUrl({
|
||||
type,
|
||||
location: {
|
||||
url: 1,
|
||||
},
|
||||
})
|
||||
).rejects.toThrow('Block type "Type" url definition should be a string.');
|
||||
});
|
||||
|
||||
test('fetchMetaUrl throws if response returns a 404 not found', async () => {
|
||||
await expect(
|
||||
fetchMetaUrl({
|
||||
type,
|
||||
location: {
|
||||
url: '404',
|
||||
},
|
||||
})
|
||||
).rejects.toThrow('Meta for type "Type" could not be found at {"url":"404"}.');
|
||||
});
|
||||
|
@ -25,6 +25,7 @@ Steps to fetch meta
|
||||
- return
|
||||
*/
|
||||
|
||||
import { type as typeHelper } from '@lowdefy/helpers';
|
||||
import createFetchMetaCache from './fetchMetaCache';
|
||||
import createWriteMetaCache from './writeMetaCache';
|
||||
import defaultMetaLocations from './defaultMetaLocations';
|
||||
@ -38,13 +39,14 @@ function createGetMeta({ types, cacheDirectory }) {
|
||||
const fetchMetaCache = createFetchMetaCache({ cacheDirectory });
|
||||
const writeMetaCache = createWriteMetaCache({ cacheDirectory });
|
||||
async function getMeta(type) {
|
||||
if (!metaLocations[type]) {
|
||||
const location = metaLocations[type];
|
||||
if (!location) {
|
||||
throw new Error(
|
||||
`Type ${JSON.stringify(type)} is not defined. Specify type url in types array.`
|
||||
`Block type ${JSON.stringify(type)} is not defined. Specify type url in types array.`
|
||||
);
|
||||
}
|
||||
let meta;
|
||||
const location = metaLocations[type];
|
||||
|
||||
meta = await fetchMetaCache(location);
|
||||
|
||||
if (meta)
|
||||
@ -52,16 +54,17 @@ function createGetMeta({ types, cacheDirectory }) {
|
||||
type,
|
||||
meta,
|
||||
};
|
||||
meta = await fetchMetaUrl(location);
|
||||
if (meta) {
|
||||
await writeMetaCache({ location, meta });
|
||||
meta = await fetchMetaUrl({ location, type });
|
||||
await writeMetaCache({ location, meta });
|
||||
// TODO: implement Ajv schema check. Use testAjvSchema func from @lowdefy/graphql
|
||||
if (meta && typeHelper.isString(meta.category) && meta.moduleFederation) {
|
||||
return {
|
||||
type,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
throw new Error(
|
||||
`Meta for type ${type} could not be found at location ${JSON.stringify(location)}.`
|
||||
`Block type ${JSON.stringify(type)} has invalid block meta at ${JSON.stringify(location)}.`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,16 @@ const types = {
|
||||
},
|
||||
};
|
||||
|
||||
const defaultMeta = {
|
||||
category: 'input',
|
||||
moduleFederation: {
|
||||
module: 'Module',
|
||||
scope: 'scope',
|
||||
version: '1.0.0',
|
||||
remoteEntryUrl: `http://localhost:3002/remoteEntry.js`,
|
||||
},
|
||||
};
|
||||
|
||||
const getMeta = createGetMeta({ types, cacheDirectory: 'cacheDirectory' });
|
||||
|
||||
beforeEach(() => {
|
||||
@ -56,36 +66,28 @@ beforeEach(() => {
|
||||
test('getMeta cache returns from cache', async () => {
|
||||
mockFetchMetaCache.mockImplementation((location) => {
|
||||
if (location && location.url === 'type1Url') {
|
||||
return {
|
||||
key: 'value',
|
||||
};
|
||||
return defaultMeta;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const res = await getMeta('Type1');
|
||||
expect(res).toEqual({
|
||||
type: 'Type1',
|
||||
meta: {
|
||||
key: 'value',
|
||||
},
|
||||
meta: defaultMeta,
|
||||
});
|
||||
});
|
||||
|
||||
test('getMeta fetches from url and writes to cache', async () => {
|
||||
mockFetchMetaUrl.mockImplementation((location) => {
|
||||
mockFetchMetaUrl.mockImplementation(({ location }) => {
|
||||
if (location && location.url === 'type1Url') {
|
||||
return {
|
||||
key: 'value',
|
||||
};
|
||||
return defaultMeta;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const res = await getMeta('Type1');
|
||||
expect(res).toEqual({
|
||||
type: 'Type1',
|
||||
meta: {
|
||||
key: 'value',
|
||||
},
|
||||
meta: defaultMeta,
|
||||
});
|
||||
expect(mockWriteMetaCache.mock.calls).toEqual([
|
||||
[
|
||||
@ -93,9 +95,7 @@ test('getMeta fetches from url and writes to cache', async () => {
|
||||
location: {
|
||||
url: 'type1Url',
|
||||
},
|
||||
meta: {
|
||||
key: 'value',
|
||||
},
|
||||
meta: defaultMeta,
|
||||
},
|
||||
],
|
||||
]);
|
||||
@ -103,18 +103,25 @@ test('getMeta fetches from url and writes to cache', async () => {
|
||||
|
||||
test('getMeta type not in types', async () => {
|
||||
await expect(getMeta('Undefined')).rejects.toThrow(
|
||||
'Type "Undefined" is not defined. Specify type url in types array.'
|
||||
'Block type "Undefined" is not defined. Specify type url in types array.'
|
||||
);
|
||||
});
|
||||
|
||||
test('getMeta undefined type', async () => {
|
||||
await expect(getMeta()).rejects.toThrow(
|
||||
'Type undefined is not defined. Specify type url in types array.'
|
||||
'Block type undefined is not defined. Specify type url in types array.'
|
||||
);
|
||||
});
|
||||
|
||||
test('getMeta meta not found in cache or url', async () => {
|
||||
await expect(getMeta('Type2')).rejects.toThrow(
|
||||
'Meta for type Type2 could not be found at location {"url":"type2Url"}.'
|
||||
'Block type "Type2" has invalid block meta at {"url":"type2Url"}.'
|
||||
);
|
||||
});
|
||||
|
||||
test('getMeta invalid meta', async () => {
|
||||
mockFetchMetaUrl.mockImplementation(() => ({ invalidMeta: true }));
|
||||
await expect(getMeta('Type2')).rejects.toThrow(
|
||||
'Block type "Type2" has invalid block meta at {"url":"type2Url"}.'
|
||||
);
|
||||
});
|
||||
|
@ -59,6 +59,7 @@
|
||||
"inquirer": "7.3.3",
|
||||
"js-yaml": "3.14.0",
|
||||
"opener": "1.5.2",
|
||||
"ora": "5.1.0",
|
||||
"reload": "3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -27,7 +27,8 @@ async function build(options) {
|
||||
configDirectory: context.baseDirectory,
|
||||
outputDirectory: context.outputDirectory,
|
||||
});
|
||||
context.print.info(`Build artifacts saved at ${context.outputDirectory}.`);
|
||||
context.print.log(`Build artifacts saved at ${context.outputDirectory}.`);
|
||||
context.print.succeed(`Build successful.`);
|
||||
}
|
||||
|
||||
export default build;
|
||||
|
@ -20,6 +20,8 @@ import getBuildScript from '../../utils/getBuildScript';
|
||||
import createContext from '../../utils/context';
|
||||
|
||||
const info = jest.fn();
|
||||
const succeed = jest.fn();
|
||||
const log = jest.fn();
|
||||
|
||||
jest.mock('../../utils/getBuildScript', () => {
|
||||
const buildScript = jest.fn();
|
||||
@ -45,6 +47,8 @@ test('build', async () => {
|
||||
createContext.mockImplementation(() => ({
|
||||
print: {
|
||||
info,
|
||||
succeed,
|
||||
log,
|
||||
},
|
||||
baseDirectory,
|
||||
cacheDirectory,
|
||||
|
@ -23,33 +23,39 @@ import getBuildScript from '../../utils/getBuildScript';
|
||||
import fetchNpmTarball from '../../utils/fetchNpmTarball';
|
||||
|
||||
async function buildNetlify(options) {
|
||||
if (process.env.NETLIFY === 'true') {
|
||||
options.basicPrint = true;
|
||||
}
|
||||
|
||||
const context = await createContext(options);
|
||||
const netlifyDir = path.resolve(context.baseDirectory, './.lowdefy/netlify');
|
||||
context.print.info('Starting Netlify build.');
|
||||
|
||||
context.print.info('Fetching lowdefy netlify server.');
|
||||
context.print.spin('Fetching Lowdefy Netlify server.');
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/server-netlify',
|
||||
version: context.version,
|
||||
directory: netlifyDir,
|
||||
});
|
||||
context.print.log('Fetched Lowdefy Netlify server.');
|
||||
|
||||
context.print.info('npm install production.');
|
||||
context.print.spin('npm install production.');
|
||||
let proccessOutput = spawnSync('npm', ['install', '--production', '--legacy-peer-deps'], {
|
||||
cwd: path.resolve(netlifyDir, 'package'),
|
||||
});
|
||||
|
||||
checkChildProcessError({
|
||||
context,
|
||||
proccessOutput,
|
||||
message: 'Failed to npm install netlify server.',
|
||||
message: 'Failed to npm install Netlify server.',
|
||||
});
|
||||
|
||||
context.print.info(proccessOutput.stdout.toString('utf8'));
|
||||
context.print.log(proccessOutput.stdout.toString('utf8'));
|
||||
|
||||
context.print.info('Fetching lowdefy build script.');
|
||||
context.print.spin('Fetching Lowdefy build script.');
|
||||
await getBuildScript(context);
|
||||
context.print.log('Fetched Lowdefy build script.');
|
||||
|
||||
context.print.info('Starting lowdefy build.');
|
||||
context.print.spin('Starting Lowdefy build.');
|
||||
const outputDirectory = path.resolve(netlifyDir, './package/dist/functions/graphql/build');
|
||||
await context.buildScript({
|
||||
logger: context.print,
|
||||
@ -57,20 +63,9 @@ async function buildNetlify(options) {
|
||||
configDirectory: context.baseDirectory,
|
||||
outputDirectory,
|
||||
});
|
||||
context.print.info(`Build artifacts saved at ${outputDirectory}.`);
|
||||
|
||||
context.print.info(`Moving output artifacts.`);
|
||||
proccessOutput = spawnSync('cp', [
|
||||
'-r',
|
||||
path.resolve(netlifyDir, 'package/dist/functions'),
|
||||
path.resolve('./.lowdefy/functions'),
|
||||
]);
|
||||
checkChildProcessError({
|
||||
context,
|
||||
proccessOutput,
|
||||
message: 'Failed to move functions artifacts.',
|
||||
});
|
||||
context.print.log(`Build artifacts saved at ${outputDirectory}.`);
|
||||
|
||||
context.print.log(`Moving output artifacts.`);
|
||||
proccessOutput = spawnSync('cp', [
|
||||
'-r',
|
||||
path.resolve(netlifyDir, 'package/dist/shell'),
|
||||
@ -81,6 +76,19 @@ async function buildNetlify(options) {
|
||||
proccessOutput,
|
||||
message: 'Failed to move publish artifacts.',
|
||||
});
|
||||
context.print.log(`Netlify publish artifacts moved to "./lowdefy/publish".`);
|
||||
|
||||
proccessOutput = spawnSync('cp', [
|
||||
'-r',
|
||||
path.resolve(netlifyDir, 'package/dist/functions'),
|
||||
path.resolve('./.lowdefy/functions'),
|
||||
]);
|
||||
checkChildProcessError({
|
||||
context,
|
||||
proccessOutput,
|
||||
message: 'Failed to move functions artifacts.',
|
||||
});
|
||||
context.print.log(`Netlify functions artifacts moved to "./lowdefy/functions".`);
|
||||
|
||||
proccessOutput = spawnSync('cp', [
|
||||
'-r',
|
||||
@ -93,7 +101,7 @@ async function buildNetlify(options) {
|
||||
message: 'Failed to move node_modules.',
|
||||
});
|
||||
|
||||
context.print.info(`Netlify build completed successfully.`);
|
||||
context.print.succeed(`Netlify build completed successfully.`);
|
||||
}
|
||||
|
||||
export default buildNetlify;
|
||||
|
@ -26,9 +26,9 @@ async function cleanCache(program) {
|
||||
}
|
||||
const print = createPrint();
|
||||
const cacheDir = path.resolve(baseDirectory, cacheDirectoryPath);
|
||||
print.info(`Cleaning cache at "${cacheDir}".`);
|
||||
print.log(`Cleaning cache at "${cacheDir}".`);
|
||||
await cleanDirectory(cacheDir);
|
||||
print.info(`Cache cleaned.`);
|
||||
print.succeed(`Cache cleaned.`);
|
||||
}
|
||||
|
||||
export default cleanCache;
|
||||
|
@ -24,9 +24,11 @@ jest.mock('@lowdefy/node-utils', () => {
|
||||
});
|
||||
|
||||
jest.mock('../../utils/print', () => {
|
||||
const info = jest.fn();
|
||||
const log = jest.fn();
|
||||
const succeed = jest.fn();
|
||||
return () => ({
|
||||
info,
|
||||
log,
|
||||
succeed,
|
||||
});
|
||||
});
|
||||
|
||||
@ -40,18 +42,14 @@ test('cleanCache', async () => {
|
||||
await cleanCache({});
|
||||
const cachePath = path.resolve(process.cwd(), './.lowdefy/.cache');
|
||||
expect(cleanDirectory.mock.calls).toEqual([[cachePath]]);
|
||||
expect(print.info.mock.calls).toEqual([
|
||||
[`Cleaning cache at "${cachePath}".`],
|
||||
['Cache cleaned.'],
|
||||
]);
|
||||
expect(print.log.mock.calls).toEqual([[`Cleaning cache at "${cachePath}".`]]);
|
||||
expect(print.succeed.mock.calls).toEqual([['Cache cleaned.']]);
|
||||
});
|
||||
|
||||
test('cleanCache baseDir', async () => {
|
||||
await cleanCache({ baseDirectory: 'baseDir' });
|
||||
const cachePath = path.resolve(process.cwd(), 'baseDir/.lowdefy/.cache');
|
||||
expect(cleanDirectory.mock.calls).toEqual([[cachePath]]);
|
||||
expect(print.info.mock.calls).toEqual([
|
||||
[`Cleaning cache at "${cachePath}".`],
|
||||
['Cache cleaned.'],
|
||||
]);
|
||||
expect(print.log.mock.calls).toEqual([[`Cleaning cache at "${cachePath}".`]]);
|
||||
expect(print.succeed.mock.calls).toEqual([['Cache cleaned.']]);
|
||||
});
|
||||
|
@ -35,7 +35,7 @@ async function dev(options) {
|
||||
await getBuildScript(context);
|
||||
await getGraphql(context);
|
||||
|
||||
context.print.info('Starting development server.');
|
||||
context.print.log('Starting Lowdefy development server.');
|
||||
|
||||
//Graphql
|
||||
const config = {
|
||||
@ -62,13 +62,14 @@ async function dev(options) {
|
||||
|
||||
// File watcher
|
||||
const fn = async () => {
|
||||
context.print.info('Building configuration.');
|
||||
context.print.log('Building configuration.');
|
||||
await context.buildScript({
|
||||
logger: context.print,
|
||||
cacheDirectory: context.cacheDirectory,
|
||||
configDirectory: context.baseDirectory,
|
||||
outputDirectory: path.resolve(context.baseDirectory, outputDirectoryPath),
|
||||
});
|
||||
context.print.succeed('Built succesfully.');
|
||||
reloadReturned.reload();
|
||||
};
|
||||
const batchChanges = new BatchChanges({ fn, context });
|
||||
@ -83,7 +84,7 @@ async function dev(options) {
|
||||
|
||||
// Start server
|
||||
app.listen(app.get('port'), function () {
|
||||
context.print.log(`Development server listening on port ${options.port}`);
|
||||
context.print.info(`Development server listening on port ${options.port}`);
|
||||
});
|
||||
opener(`http://localhost:${options.port}`);
|
||||
}
|
||||
|
@ -23,11 +23,13 @@ async function getGraphql(context) {
|
||||
const cleanVersion = context.version.replace(/[-.]/g, '_');
|
||||
const cachePath = path.resolve(context.cacheDirectory, `scripts/graphql_${cleanVersion}`);
|
||||
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
|
||||
context.print.spin(`Fetching @lowdefy/graphql@${context.version} to cache.`);
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/graphql',
|
||||
version: context.version,
|
||||
directory: cachePath,
|
||||
});
|
||||
context.print.log(`Fetched @lowdefy/build@${context.version} to cache.`);
|
||||
}
|
||||
context.graphql = await loadModule(
|
||||
path.resolve(cachePath, 'package/dist/moduleFederation'),
|
||||
|
@ -27,7 +27,7 @@ dotenv.config({ silent: true });
|
||||
|
||||
const { description, version } = packageJson;
|
||||
|
||||
program.description(description).version(version, '-v, --version');
|
||||
program.name('lowdefy').description(description).version(version, '-v, --version');
|
||||
|
||||
program
|
||||
.command('build')
|
||||
@ -75,6 +75,6 @@ program
|
||||
'Change base directory. Default is the current working directory.'
|
||||
)
|
||||
.passCommandToAction(false)
|
||||
.action(errorHandler(dev, { stayAlive: true }));
|
||||
.action(errorHandler(dev));
|
||||
|
||||
program.parse(process.argv);
|
||||
|
50
packages/cli/src/utils/checkChildProcessError.test.js
Normal file
50
packages/cli/src/utils/checkChildProcessError.test.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import checkChildProcessError from './checkChildProcessError';
|
||||
|
||||
const mockError = jest.fn();
|
||||
const context = {
|
||||
print: {
|
||||
error: mockError,
|
||||
},
|
||||
};
|
||||
|
||||
test('output status 0', () => {
|
||||
checkChildProcessError({ context, proccessOutput: { status: 0 }, message: 'Test Error Message' });
|
||||
// checkChildProcessError should not throw, expect is so that test passes
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test('output status 1', () => {
|
||||
const proccessOutput = {
|
||||
status: 1,
|
||||
stderr: Buffer.from('Process error message'),
|
||||
};
|
||||
expect(() =>
|
||||
checkChildProcessError({ context, proccessOutput, message: 'Test Error Message' })
|
||||
).toThrow('Test Error Message');
|
||||
expect(mockError.mock.calls).toMatchInlineSnapshot(
|
||||
[['Process error message']],
|
||||
`
|
||||
Array [
|
||||
Array [
|
||||
"Process error message",
|
||||
],
|
||||
]
|
||||
`
|
||||
);
|
||||
});
|
@ -20,12 +20,14 @@ import createPrint from './print';
|
||||
import { cacheDirectoryPath, outputDirectoryPath } from './directories';
|
||||
|
||||
async function createContext(options = {}) {
|
||||
const context = {
|
||||
baseDirectory: path.resolve(options.baseDirectory || process.cwd()),
|
||||
print: createPrint({ timestamp: true }),
|
||||
};
|
||||
context.cacheDirectory = path.resolve(context.baseDirectory, cacheDirectoryPath);
|
||||
const context = {};
|
||||
|
||||
context.print = createPrint({
|
||||
basic: options.basicPrint,
|
||||
});
|
||||
|
||||
context.baseDirectory = path.resolve(options.baseDirectory || process.cwd());
|
||||
context.cacheDirectory = path.resolve(context.baseDirectory, cacheDirectoryPath);
|
||||
if (options.outputDirectory) {
|
||||
context.outputDirectory = path.resolve(options.outputDirectory);
|
||||
} else {
|
||||
|
@ -29,16 +29,32 @@ async function fetchNpmTarball({ name, version, directory }) {
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!packageInfo || !packageInfo.data) {
|
||||
throw new Error(`Package "${name}" could not be found at ${registryUrl}.`);
|
||||
}
|
||||
|
||||
if (!packageInfo.data.versions[version]) {
|
||||
throw new Error(`Invalid version. "${name}" does not have version "${version}".`);
|
||||
}
|
||||
const tarball = await axios.get(packageInfo.data.versions[version].dist.tarball, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
let tarball;
|
||||
|
||||
try {
|
||||
tarball = await axios.get(packageInfo.data.versions[version].dist.tarball, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 404) {
|
||||
throw new Error(
|
||||
`Package "${name}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!tarball || !tarball.data) {
|
||||
/// TODO: Check if user has internet connection.
|
||||
throw new Error(
|
||||
`Tarball could not be fetched from "${packageInfo.data.versions[version].dist.tarball}". Check internet connection.`
|
||||
`Package "${name}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
);
|
||||
}
|
||||
await decompress(tarball.data, directory, {
|
||||
|
154
packages/cli/src/utils/fetchNpmTarball.test.js
Normal file
154
packages/cli/src/utils/fetchNpmTarball.test.js
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import axios from 'axios';
|
||||
import decompress from 'decompress';
|
||||
import decompressTargz from 'decompress-targz';
|
||||
import fetchNpmTarball from './fetchNpmTarball';
|
||||
|
||||
// TODO: not testing decompress
|
||||
|
||||
jest.mock('decompress');
|
||||
jest.mock('decompress-targz');
|
||||
|
||||
const directory = 'directory';
|
||||
|
||||
jest.mock('axios', () => {
|
||||
return {
|
||||
get: (url) => {
|
||||
if (url === 'https://registry.npmjs.org/valid-package') {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
versions: {
|
||||
'1.0.0': {
|
||||
dist: {
|
||||
tarball: 'tarball-url',
|
||||
},
|
||||
},
|
||||
v404: {
|
||||
dist: {
|
||||
tarball: 'https://registry.npmjs.org/404',
|
||||
},
|
||||
},
|
||||
noData: {
|
||||
dist: {
|
||||
tarball: 'https://registry.npmjs.org/no-data',
|
||||
},
|
||||
},
|
||||
undef: {
|
||||
dist: {
|
||||
tarball: 'https://registry.npmjs.org/undefined',
|
||||
},
|
||||
},
|
||||
error: {
|
||||
dist: {
|
||||
tarball: 'https://registry.npmjs.org/axios-error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (url === 'tarball-url') {
|
||||
return {
|
||||
data: Buffer.from('tarball data'),
|
||||
};
|
||||
}
|
||||
if (url === 'https://registry.npmjs.org/404') {
|
||||
const error = new Error('Test 404');
|
||||
error.response = {};
|
||||
error.response.status = 404;
|
||||
throw error;
|
||||
}
|
||||
if (url === 'https://registry.npmjs.org/axios-error') {
|
||||
throw new Error('Axios error');
|
||||
}
|
||||
if (url === 'https://registry.npmjs.org/no-data') {
|
||||
return {};
|
||||
}
|
||||
if (url === 'https://registry.npmjs.org/undefined') {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
test('valid package and version', async () => {
|
||||
await fetchNpmTarball({ name: 'valid-package', version: '1.0.0', directory });
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test('version does not exist', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'invalid', directory })
|
||||
).rejects.toThrow('Invalid version. "valid-package" does not have version "invalid"');
|
||||
});
|
||||
|
||||
test('npm return a 404', async () => {
|
||||
await expect(fetchNpmTarball({ name: '404', version: '1.0.0', directory })).rejects.toThrow(
|
||||
'Package "404" could not be found at https://registry.npmjs.org/404.'
|
||||
);
|
||||
});
|
||||
|
||||
test('axios error', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'axios-error', version: '1.0.0', directory })
|
||||
).rejects.toThrow('Axios error');
|
||||
});
|
||||
|
||||
test('empty response', async () => {
|
||||
await expect(fetchNpmTarball({ name: 'no-data', version: '1.0.0', directory })).rejects.toThrow(
|
||||
'Package "no-data" could not be found at https://registry.npmjs.org/no-data.'
|
||||
);
|
||||
});
|
||||
|
||||
test('undefined response', async () => {
|
||||
await expect(fetchNpmTarball({ name: 'undefined', version: '1.0.0', directory })).rejects.toThrow(
|
||||
'Package "undefined" could not be found at https://registry.npmjs.org/undefined.'
|
||||
);
|
||||
});
|
||||
|
||||
test('tarball 404', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'v404', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/404.'
|
||||
);
|
||||
});
|
||||
|
||||
test('tarball axios error', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'error', directory })
|
||||
).rejects.toThrow('Axios error');
|
||||
});
|
||||
|
||||
test('tarball empty response', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'noData', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/no-data.'
|
||||
);
|
||||
});
|
||||
|
||||
test('tarball undefined response', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'undef', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/undefined.'
|
||||
);
|
||||
});
|
@ -23,11 +23,13 @@ async function getBuildScript(context) {
|
||||
const cleanVersion = context.version.replace(/[-.]/g, '_');
|
||||
const cachePath = path.resolve(context.cacheDirectory, `scripts/build_${cleanVersion}`);
|
||||
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
|
||||
context.print.spin(`Fetching @lowdefy/build@${context.version} to cache.`);
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/build',
|
||||
version: context.version,
|
||||
directory: cachePath,
|
||||
});
|
||||
context.print.log(`Fetched @lowdefy/build@${context.version} to cache.`);
|
||||
}
|
||||
const buildScript = await loadModule(path.resolve(cachePath, 'package/dist'), './build');
|
||||
context.buildScript = buildScript.default;
|
||||
|
@ -14,29 +14,48 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import ora from 'ora';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const printToTerminal = (color, options = {}) => (text) => {
|
||||
let message;
|
||||
if (options.timestamp) {
|
||||
const time = new Date(Date.now());
|
||||
const h = time.getHours();
|
||||
const m = time.getMinutes();
|
||||
const s = time.getSeconds();
|
||||
const timeString = `${h > 9 ? '' : '0'}${h}:${m > 9 ? '' : '0'}${m}:${s > 9 ? '' : '0'}${s}`;
|
||||
message = `${chalk.dim(timeString)} - ${color(text)}`;
|
||||
} else {
|
||||
message = color(text);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(message);
|
||||
};
|
||||
function getTime() {
|
||||
const time = new Date(Date.now());
|
||||
const h = time.getHours();
|
||||
const m = time.getMinutes();
|
||||
const s = time.getSeconds();
|
||||
return `${h > 9 ? '' : '0'}${h}:${m > 9 ? '' : '0'}${m}:${s > 9 ? '' : '0'}${s}`;
|
||||
}
|
||||
|
||||
const createPrint = (options) => ({
|
||||
info: printToTerminal(chalk.blue, options),
|
||||
log: printToTerminal(chalk.green, options),
|
||||
warn: printToTerminal(chalk.yellow, options),
|
||||
error: printToTerminal(chalk.red, options),
|
||||
});
|
||||
function createOraPrint() {
|
||||
const spinner = ora({
|
||||
spinner: 'random',
|
||||
prefixText: () => chalk.dim(getTime()),
|
||||
color: 'blue',
|
||||
});
|
||||
return {
|
||||
error: (text) => spinner.fail(chalk.red(text)),
|
||||
info: (text) => spinner.info(chalk.blue(text)),
|
||||
log: (text) => spinner.start(text).stopAndPersist({ symbol: '∙' }),
|
||||
spin: (text) => spinner.start(text),
|
||||
succeed: (text) => spinner.succeed(chalk.green(text)),
|
||||
warn: (text) => spinner.warn(chalk.yellow(text)),
|
||||
};
|
||||
}
|
||||
|
||||
function createBasicPrint() {
|
||||
const { error, info, log, warn } = console;
|
||||
return {
|
||||
error,
|
||||
info,
|
||||
log,
|
||||
spin: log,
|
||||
succeed: log,
|
||||
warn,
|
||||
};
|
||||
}
|
||||
|
||||
function createPrint({ basic } = {}) {
|
||||
if (basic) return createBasicPrint();
|
||||
return createOraPrint();
|
||||
}
|
||||
|
||||
export default createPrint;
|
||||
|
@ -14,15 +14,48 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import ora from 'ora';
|
||||
import createPrint from './print';
|
||||
|
||||
jest.mock('ora', () => {
|
||||
const mockOraConstructor = jest.fn();
|
||||
return mockOraConstructor;
|
||||
});
|
||||
|
||||
const mockOraFail = jest.fn();
|
||||
const mockOraInfo = jest.fn();
|
||||
const mockOraStart = jest.fn();
|
||||
const mockOraStopAndPersist = jest.fn();
|
||||
const mockOraSucceed = jest.fn();
|
||||
const mockOraWarn = jest.fn();
|
||||
|
||||
ora.mockImplementation(() => ({
|
||||
fail: mockOraFail,
|
||||
info: mockOraInfo,
|
||||
start: mockOraStart,
|
||||
stopAndPersist: mockOraStopAndPersist,
|
||||
succeed: mockOraSucceed,
|
||||
warn: mockOraWarn,
|
||||
}));
|
||||
|
||||
mockOraStart.mockImplementation(() => ({ stopAndPersist: mockOraStopAndPersist }));
|
||||
|
||||
// mock console
|
||||
const mockConsoleError = jest.fn();
|
||||
const mockConsoleInfo = jest.fn();
|
||||
const mockConsoleLog = jest.fn();
|
||||
const mockConsoleWarn = jest.fn();
|
||||
console.error = mockConsoleError;
|
||||
console.info = mockConsoleInfo;
|
||||
console.log = mockConsoleLog;
|
||||
console.warn = mockConsoleWarn;
|
||||
|
||||
// Mock timestamps
|
||||
const mockGetHours = jest.fn();
|
||||
const mockGetMinutes = jest.fn();
|
||||
const mockGetSeconds = jest.fn();
|
||||
// const realLog = console.log;
|
||||
// const realNow = Date.now;
|
||||
console.log = mockConsoleLog;
|
||||
// eslint-disable-next-line no-global-assign
|
||||
Date = jest.fn(() => ({
|
||||
getHours: mockGetHours,
|
||||
@ -33,7 +66,6 @@ Date = jest.fn(() => ({
|
||||
Date.now = () => {};
|
||||
|
||||
beforeEach(() => {
|
||||
mockConsoleLog.mockReset();
|
||||
mockGetHours.mockReset();
|
||||
mockGetMinutes.mockReset();
|
||||
mockGetSeconds.mockReset();
|
||||
@ -44,110 +76,138 @@ beforeEach(() => {
|
||||
// Date.now = realNow;
|
||||
// });
|
||||
|
||||
test('create print', () => {
|
||||
const print = createPrint();
|
||||
expect(print).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"error": [Function],
|
||||
"info": [Function],
|
||||
"log": [Function],
|
||||
"warn": [Function],
|
||||
}
|
||||
`);
|
||||
describe('ora print', () => {
|
||||
test('create print', () => {
|
||||
const print = createPrint();
|
||||
expect(print).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"error": [Function],
|
||||
"info": [Function],
|
||||
"log": [Function],
|
||||
"spin": [Function],
|
||||
"succeed": [Function],
|
||||
"warn": [Function],
|
||||
}
|
||||
`);
|
||||
expect(ora.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"color": "blue",
|
||||
"prefixText": [Function],
|
||||
"spinner": "random",
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('timestamp, digits less than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 1);
|
||||
mockGetMinutes.mockImplementation(() => 2);
|
||||
mockGetSeconds.mockImplementation(() => 3);
|
||||
createPrint();
|
||||
const prefixTextFn = ora.mock.calls[0][0].prefixText;
|
||||
const res = prefixTextFn();
|
||||
expect(res).toEqual('[2m01:02:03[22m');
|
||||
});
|
||||
|
||||
test('timestamp, digits more than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 11);
|
||||
mockGetMinutes.mockImplementation(() => 22);
|
||||
mockGetSeconds.mockImplementation(() => 33);
|
||||
createPrint();
|
||||
const prefixTextFn = ora.mock.calls[0][0].prefixText;
|
||||
const res = prefixTextFn();
|
||||
expect(res).toEqual('[2m11:22:33[22m');
|
||||
});
|
||||
|
||||
test('print error', () => {
|
||||
const print = createPrint();
|
||||
print.error('Test error');
|
||||
expect(mockOraFail.mock.calls).toEqual([['[31mTest error[39m']]);
|
||||
});
|
||||
|
||||
test('print info', () => {
|
||||
const print = createPrint();
|
||||
print.info('Test info');
|
||||
expect(mockOraInfo.mock.calls).toEqual([['[34mTest info[39m']]);
|
||||
});
|
||||
|
||||
test('print log', () => {
|
||||
const print = createPrint();
|
||||
print.log('Test log');
|
||||
expect(mockOraStart.mock.calls).toEqual([['Test log']]);
|
||||
expect(mockOraStopAndPersist.mock.calls).toEqual([[{ symbol: '∙' }]]);
|
||||
});
|
||||
|
||||
test('print spin', () => {
|
||||
const print = createPrint();
|
||||
print.spin('Test spin');
|
||||
expect(mockOraStart.mock.calls).toEqual([['Test spin']]);
|
||||
});
|
||||
|
||||
test('print succeed', () => {
|
||||
const print = createPrint();
|
||||
print.succeed('Test succeed');
|
||||
expect(mockOraSucceed.mock.calls).toEqual([['[32mTest succeed[39m']]);
|
||||
});
|
||||
|
||||
test('print warn', () => {
|
||||
const print = createPrint();
|
||||
print.warn('Test warn');
|
||||
expect(mockOraWarn.mock.calls).toEqual([['[33mTest warn[39m']]);
|
||||
});
|
||||
});
|
||||
|
||||
test('print info', () => {
|
||||
const print = createPrint();
|
||||
print.info('Test info');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[34mTest info[39m']]);
|
||||
});
|
||||
describe('basic print', () => {
|
||||
test('create print', () => {
|
||||
const print = createPrint();
|
||||
expect(print).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"error": [Function],
|
||||
"info": [Function],
|
||||
"log": [Function],
|
||||
"spin": [Function],
|
||||
"succeed": [Function],
|
||||
"warn": [Function],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('print log', () => {
|
||||
const print = createPrint();
|
||||
print.log('Test log');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[32mTest log[39m']]);
|
||||
});
|
||||
test('print error', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.error('Test error');
|
||||
expect(mockConsoleError.mock.calls).toEqual([['Test error']]);
|
||||
});
|
||||
|
||||
test('print warn', () => {
|
||||
const print = createPrint();
|
||||
print.warn('Test warn');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[33mTest warn[39m']]);
|
||||
});
|
||||
test('print info', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.info('Test info');
|
||||
expect(mockConsoleInfo.mock.calls).toEqual([['Test info']]);
|
||||
});
|
||||
|
||||
test('print error', () => {
|
||||
const print = createPrint();
|
||||
print.error('Test error');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[31mTest error[39m']]);
|
||||
});
|
||||
test('print log', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.log('Test log');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['Test log']]);
|
||||
});
|
||||
|
||||
test('print info with timestamp, less than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 1);
|
||||
mockGetMinutes.mockImplementation(() => 2);
|
||||
mockGetSeconds.mockImplementation(() => 3);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.info('Test info');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m01:02:03[22m - [34mTest info[39m']]);
|
||||
});
|
||||
test('print spin', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.spin('Test spin');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['Test spin']]);
|
||||
});
|
||||
|
||||
test('print log with timestamp, less than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 1);
|
||||
mockGetMinutes.mockImplementation(() => 2);
|
||||
mockGetSeconds.mockImplementation(() => 3);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.log('Test log');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m01:02:03[22m - [32mTest log[39m']]);
|
||||
});
|
||||
test('print succeed', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.succeed('Test succeed');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['Test succeed']]);
|
||||
});
|
||||
|
||||
test('print warn with timestamp, less than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 1);
|
||||
mockGetMinutes.mockImplementation(() => 2);
|
||||
mockGetSeconds.mockImplementation(() => 3);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.warn('Test warn');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m01:02:03[22m - [33mTest warn[39m']]);
|
||||
});
|
||||
|
||||
test('print error with timestamp, less than 10', () => {
|
||||
mockGetHours.mockImplementation(() => 1);
|
||||
mockGetMinutes.mockImplementation(() => 2);
|
||||
mockGetSeconds.mockImplementation(() => 3);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.error('Test error');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m01:02:03[22m - [31mTest error[39m']]);
|
||||
});
|
||||
|
||||
test('print info with timestamp, two digits', () => {
|
||||
mockGetHours.mockImplementation(() => 11);
|
||||
mockGetMinutes.mockImplementation(() => 22);
|
||||
mockGetSeconds.mockImplementation(() => 33);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.info('Test info');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m11:22:33[22m - [34mTest info[39m']]);
|
||||
});
|
||||
|
||||
test('print log with timestamp, two digits', () => {
|
||||
mockGetHours.mockImplementation(() => 11);
|
||||
mockGetMinutes.mockImplementation(() => 22);
|
||||
mockGetSeconds.mockImplementation(() => 33);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.log('Test log');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m11:22:33[22m - [32mTest log[39m']]);
|
||||
});
|
||||
|
||||
test('print warn with timestamp, two digits', () => {
|
||||
mockGetHours.mockImplementation(() => 11);
|
||||
mockGetMinutes.mockImplementation(() => 22);
|
||||
mockGetSeconds.mockImplementation(() => 33);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.warn('Test warn');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m11:22:33[22m - [33mTest warn[39m']]);
|
||||
});
|
||||
|
||||
test('print error with timestamp, two digits', () => {
|
||||
mockGetHours.mockImplementation(() => 11);
|
||||
mockGetMinutes.mockImplementation(() => 22);
|
||||
mockGetSeconds.mockImplementation(() => 33);
|
||||
const print = createPrint({ timestamp: true });
|
||||
print.error('Test error');
|
||||
expect(mockConsoleLog.mock.calls).toEqual([['[2m11:22:33[22m - [31mTest error[39m']]);
|
||||
test('print warn', () => {
|
||||
const print = createPrint({ basic: true });
|
||||
print.warn('Test warn');
|
||||
expect(mockConsoleWarn.mock.calls).toEqual([['Test warn']]);
|
||||
});
|
||||
});
|
||||
|
42
yarn.lock
42
yarn.lock
@ -2995,6 +2995,7 @@ __metadata:
|
||||
jest: 26.6.3
|
||||
js-yaml: 3.14.0
|
||||
opener: 1.5.2
|
||||
ora: 5.1.0
|
||||
react: 17.0.1
|
||||
react-dom: 17.0.1
|
||||
reload: 3.1.1
|
||||
@ -6860,6 +6861,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-spinners@npm:^2.4.0":
|
||||
version: 2.5.0
|
||||
resolution: "cli-spinners@npm:2.5.0"
|
||||
checksum: a275c3588179de0a07579742e1fedb508caa6840516761dac1f8544886d4aa025fc2d536323ac9c325624349203010e149ca8b0028be239fc45ed3a1c1252677
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-width@npm:^2.0.0":
|
||||
version: 2.2.1
|
||||
resolution: "cli-width@npm:2.2.1"
|
||||
@ -11567,6 +11575,13 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-interactive@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "is-interactive@npm:1.0.0"
|
||||
checksum: d79b435e5134ccd60dfe035117b1cddd5c5100e90b2d33428adfe1667e26f0114cc1bc7b3ff84a1b107de8ef27f155e3ecc3bb08c0e502a15c66300b4a45d9e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-natural-number@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "is-natural-number@npm:4.0.1"
|
||||
@ -13081,6 +13096,15 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"log-symbols@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "log-symbols@npm:4.0.0"
|
||||
dependencies:
|
||||
chalk: ^4.0.0
|
||||
checksum: 2cbdb0427d1853f2bd36645bff42aaca200902284f28aadacb3c0fa4c8c43fe6bfb71b5d61ab08b67063d066d7c55b8bf5fbb43b03e4a150dbcdd643e9cd1dbf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loglevel@npm:^1.6.7, loglevel@npm:^1.6.8":
|
||||
version: 1.7.1
|
||||
resolution: "loglevel@npm:1.7.1"
|
||||
@ -14856,6 +14880,22 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ora@npm:5.1.0":
|
||||
version: 5.1.0
|
||||
resolution: "ora@npm:5.1.0"
|
||||
dependencies:
|
||||
chalk: ^4.1.0
|
||||
cli-cursor: ^3.1.0
|
||||
cli-spinners: ^2.4.0
|
||||
is-interactive: ^1.0.0
|
||||
log-symbols: ^4.0.0
|
||||
mute-stream: 0.0.8
|
||||
strip-ansi: ^6.0.0
|
||||
wcwidth: ^1.0.1
|
||||
checksum: 53aad8d2996056eebb8f68ae874d101d079c05a8251ab281734b37c0252955c82f758d649b5757e54f867bbc98549545dd88b061033487af0b598d4da92d1a82
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"original@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "original@npm:1.0.2"
|
||||
@ -19946,7 +19986,7 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wcwidth@npm:^1.0.0":
|
||||
"wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "wcwidth@npm:1.0.1"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user