mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-07-18 17:05:17 +00:00
Compare commits
21 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f28803a82 | ||
|
|
943bdecd3e | ||
|
|
e4ff4a78cc | ||
|
|
fec5538f01 | ||
|
|
b7bb363aa8 | ||
|
|
cd4a2cec76 | ||
|
|
0638ca32cb | ||
|
|
fe185cc93e | ||
|
|
7edea50296 | ||
|
|
8258cdb651 | ||
|
|
a9e29eee85 | ||
|
|
a52a6aae62 | ||
|
|
92c79830f5 | ||
|
|
8629f67972 | ||
|
|
c04d363c46 | ||
|
|
046a4ef22e | ||
|
|
579c9e41d7 | ||
|
|
6014898610 | ||
|
|
ad3bf95e5f | ||
|
|
4e22a4edde | ||
|
|
9c50fe53e7 |
42 changed files with 931 additions and 136 deletions
4
.github/workflows/docker-io.yml
vendored
4
.github/workflows/docker-io.yml
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
name: Docker IO
|
name: Docker IO
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
on:
|
on:
|
||||||
- push
|
- push
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -49,7 +51,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile.io
|
file: docker/Dockerfile.io
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
coderaiser/cloudcmd:io
|
coderaiser/cloudcmd:io
|
||||||
|
|
|
||||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
name: Docker CI
|
name: Docker CI
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ export default {
|
||||||
'start:dev',
|
'start:dev',
|
||||||
]),
|
]),
|
||||||
'lint:all': () => run('lint:progress'),
|
'lint:all': () => run('lint:progress'),
|
||||||
'lint': () => 'redlint fix; putout . --rulesdir rules',
|
'lint': () => 'redlint fix; putout .',
|
||||||
'lint:progress': () => run('lint', '-f progress'),
|
'lint:progress': () => run('lint', '-f progress'),
|
||||||
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
|
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
|
||||||
'fresh:lint': () => run('lint', '--fresh'),
|
'fresh:lint': () => run('lint', '--fresh'),
|
||||||
'lint:fresh': () => run('lint', '--fresh'),
|
'lint:fresh': () => run('lint', '--fresh'),
|
||||||
'fix:lint': async () => `putout --rulesdir rules --fix . && redlint fix`,
|
'fix:lint': async () => `putout --fix . && redlint fix`,
|
||||||
'lint:stream': () => run('lint', '-f stream'),
|
'lint:stream': () => run('lint', '-f stream'),
|
||||||
'test': () => [testEnv, `tape '{test,test-e2e}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`],
|
'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`],
|
||||||
'test:e2e': () => `tape 'test-e2e/**/*.js'`,
|
'test:e2e': () => `tape 'test-e2e/**/*.js'`,
|
||||||
'test:client': () => `tape 'test/client/**/*.js'`,
|
'test:client': () => `tape 'test/client/**/*.js'`,
|
||||||
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
||||||
|
|
@ -39,7 +39,7 @@ export default {
|
||||||
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
|
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
|
||||||
'coverage:report': () => 'c8 report --reporter=lcov',
|
'coverage:report': () => 'c8 report --reporter=lcov',
|
||||||
'report': () => 'c8 report --reporter=lcov',
|
'report': () => 'c8 report --reporter=lcov',
|
||||||
'6to5': () => [buildEnv, 'webpack --progress'],
|
'6to5': () => [buildEnv, 'rspack build --config rspack.config.js'],
|
||||||
'6to5:client': () => run('6to5', '--mode production'),
|
'6to5:client': () => run('6to5', '--mode production'),
|
||||||
'6to5:client:dev': async () => await run('6to5', '--mode development', {
|
'6to5:client:dev': async () => await run('6to5', '--mode development', {
|
||||||
NODE_ENV: 'development',
|
NODE_ENV: 'development',
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
import {env} from 'node:process';
|
import {env} from 'node:process';
|
||||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
import {rspack} from '@rspack/core';
|
||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
||||||
|
|
||||||
|
const {CssExtractRspackPlugin} = rspack;
|
||||||
const isDev = env.NODE_ENV === 'development';
|
const isDev = env.NODE_ENV === 'development';
|
||||||
const clean = (a) => a.filter(Boolean);
|
|
||||||
|
|
||||||
const plugins = clean([
|
const plugins = [
|
||||||
new MiniCssExtractPlugin({
|
new CssExtractRspackPlugin({
|
||||||
filename: '[name].css',
|
filename: '[name].css',
|
||||||
}),
|
}),
|
||||||
]);
|
];
|
||||||
|
|
||||||
const rules = [{
|
const rules = [{
|
||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: [MiniCssExtractPlugin.loader, {
|
use: [CssExtractRspackPlugin.loader, {
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
url: true,
|
url: true,
|
||||||
|
|
@ -33,13 +32,7 @@ export default {
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: !isDev,
|
minimize: !isDev,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new CssMinimizerPlugin({
|
new rspack.LightningCssMinimizerRspackPlugin(),
|
||||||
minimizerOptions: {
|
|
||||||
preset: ['default', {
|
|
||||||
svgo: false,
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import {resolve, sep} from 'node:path';
|
import {resolve, sep} from 'node:path';
|
||||||
|
import {fileURLToPath} from 'node:url';
|
||||||
import {env} from 'node:process';
|
import {env} from 'node:process';
|
||||||
import webpack from 'webpack';
|
import {rspack} from '@rspack/core';
|
||||||
import WebpackBar from 'webpackbar';
|
|
||||||
|
const resolveModule = (a) => fileURLToPath(import.meta.resolve(a));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
EnvironmentPlugin,
|
EnvironmentPlugin,
|
||||||
NormalModuleReplacementPlugin,
|
NormalModuleReplacementPlugin,
|
||||||
} = webpack;
|
ProvidePlugin,
|
||||||
|
} = rspack;
|
||||||
|
|
||||||
const modules = './modules';
|
const modules = './modules';
|
||||||
const dirModules = './client/modules';
|
const dirModules = './client/modules';
|
||||||
|
|
@ -22,27 +25,31 @@ const dist = resolve(rootDir, 'dist');
|
||||||
const distDev = resolve(rootDir, 'dist-dev');
|
const distDev = resolve(rootDir, 'dist-dev');
|
||||||
const devtool = isDev ? 'eval' : 'source-map';
|
const devtool = isDev ? 'eval' : 'source-map';
|
||||||
|
|
||||||
const notEmpty = (a) => a;
|
|
||||||
const clean = (array) => array.filter(notEmpty);
|
|
||||||
|
|
||||||
const noParse = (a) => a.endsWith('.spec.js');
|
const noParse = (a) => a.endsWith('.spec.js');
|
||||||
const options = {
|
|
||||||
babelrc: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const rules = clean([
|
// codegen.macro is a babel-macro (build-time codegen), not supported by
|
||||||
!isDev && {
|
// Rspack's native SWC transform, so client/sw/sw.js (the only file that
|
||||||
test: /\.[mc]?js$/,
|
// uses it) keeps going through babel-loader. Everything else uses
|
||||||
exclude: /node_modules/,
|
// Rspack's built-in SWC loader, which is the main source of the speedup.
|
||||||
loader: 'babel-loader',
|
const rules = [{
|
||||||
|
test: /sw\/sw\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
}, {
|
||||||
|
test: /\.[mc]?js$/,
|
||||||
|
exclude: [/node_modules/, /sw\/sw\.js$/],
|
||||||
|
loader: 'builtin:swc-loader',
|
||||||
|
options: {
|
||||||
|
jsc: {
|
||||||
|
parser: {
|
||||||
|
syntax: 'ecmascript',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
targets: 'defaults',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
isDev && {
|
}];
|
||||||
test: /\.[mc]?js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
|
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
|
||||||
|
|
@ -52,8 +59,7 @@ const plugins = [
|
||||||
new EnvironmentPlugin({
|
new EnvironmentPlugin({
|
||||||
NODE_ENV,
|
NODE_ENV,
|
||||||
}),
|
}),
|
||||||
new WebpackBar(),
|
new ProvidePlugin({
|
||||||
new webpack.ProvidePlugin({
|
|
||||||
process: 'process/browser',
|
process: 'process/browser',
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
@ -99,9 +105,9 @@ export default {
|
||||||
'node:path': 'path',
|
'node:path': 'path',
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
path: import.meta.resolve('path-browserify'),
|
path: resolveModule('path-browserify'),
|
||||||
process: import.meta.resolve('process/browser'),
|
process: resolveModule('process/browser'),
|
||||||
util: import.meta.resolve('util'),
|
util: resolveModule('util'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devtool,
|
devtool,
|
||||||
|
|
@ -156,7 +162,7 @@ export default {
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
performance: {
|
performance: {
|
||||||
maxEntrypointSize: 600_000,
|
maxEntrypointSize: 800_000,
|
||||||
maxAssetSize: 600_000,
|
maxAssetSize: 600_000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -19,14 +19,13 @@ test('current-file: setCurrentName: setAttribute', (t) => {
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(setAttribute, [
|
t.calledWith(setAttribute, [
|
||||||
'data-name',
|
'data-name',
|
||||||
'js-file-aGVsbG8=',
|
'js-file-aGVsbG8=',
|
||||||
], 'should call setAttribute');
|
], 'should call setAttribute');
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -41,14 +40,13 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
|
||||||
|
|
||||||
currentFile.setCurrentName('ай', current);
|
currentFile.setCurrentName('ай', current);
|
||||||
|
|
||||||
|
globalThis.DOM = DOM;
|
||||||
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(setAttribute, [
|
t.calledWith(setAttribute, [
|
||||||
'data-name',
|
'data-name',
|
||||||
'js-file-JUQwJUIwJUQwJUI5',
|
'js-file-JUQwJUIwJUQwJUI5',
|
||||||
], 'should call setAttribute');
|
], 'should call setAttribute');
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -76,11 +74,10 @@ test('current-file: emit', (t) => {
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
t.calledWith(emit, ['current-file', current], 'should call emit');
|
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.calledWith(emit, ['current-file', current], 'should call emit');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -96,14 +93,12 @@ test('current-file: setCurrentName: return', (t) => {
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.setCurrentName('hello', current);
|
const result = currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
t.equal(result, link, 'should return link');
|
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
globalThis.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
globalThis.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
|
t.equal(result, link, 'should return link');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
import test from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import {create} from 'auto-globals';
|
import {create} from 'auto-globals';
|
||||||
import {tryCatch} from 'try-catch';
|
import {tryCatch} from 'try-catch';
|
||||||
import {isContainClass} from './dom-tree.js';
|
import {
|
||||||
|
isContainClass,
|
||||||
|
getByTag,
|
||||||
|
getById,
|
||||||
|
getByClass,
|
||||||
|
getByDataName,
|
||||||
|
getByClassAll,
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
} from './dom-tree.js';
|
||||||
|
|
||||||
test('dom: isContainClass: no element', (t) => {
|
test('dom: isContainClass: no element', (t) => {
|
||||||
const [e] = tryCatch(isContainClass);
|
const [e] = tryCatch(isContainClass);
|
||||||
|
|
@ -38,3 +47,106 @@ test('dom: isContainClass: contains: array', (t) => {
|
||||||
t.calledWith(contains, [className], 'should call contains');
|
t.calledWith(contains, [className], 'should call contains');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('dom: getByTag', (t) => {
|
||||||
|
const getElementsByTagName = stub();
|
||||||
|
const element = {
|
||||||
|
getElementsByTagName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByTag('div', element);
|
||||||
|
|
||||||
|
t.calledWith(getElementsByTagName, ['div'], 'should call getElementsByTagName');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getById', (t) => {
|
||||||
|
const querySelector = stub();
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
getById('my-id', element);
|
||||||
|
|
||||||
|
t.calledWith(querySelector, ['#my-id'], 'should call querySelector with id selector');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClassAll', (t) => {
|
||||||
|
const getElementsByClassName = stub();
|
||||||
|
const element = {
|
||||||
|
getElementsByClassName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByClassAll('my-class', element);
|
||||||
|
|
||||||
|
t.calledWith(getElementsByClassName, ['my-class'], 'should call getElementsByClassName');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClass: calls getByClassAll', (t) => {
|
||||||
|
const element = {
|
||||||
|
getElementsByClassName: stub().returns(['first']),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getByClass('my-class', element);
|
||||||
|
|
||||||
|
t.equal(result, 'first', 'should return first element from class list');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByDataName', (t) => {
|
||||||
|
const querySelector = stub();
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByDataName('hello', element);
|
||||||
|
|
||||||
|
t.calledWith(querySelector, ['[data-name="hello"]'], 'should call querySelector with data-name selector');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: hide', (t) => {
|
||||||
|
const add = stub();
|
||||||
|
const element = {
|
||||||
|
classList: {
|
||||||
|
add,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
hide(element);
|
||||||
|
|
||||||
|
t.calledWith(add, ['hidden'], 'should add hidden class');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: show', (t) => {
|
||||||
|
const remove = stub();
|
||||||
|
const element = {
|
||||||
|
classList: {
|
||||||
|
remove,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
show(element);
|
||||||
|
|
||||||
|
t.calledWith(remove, ['hidden'], 'should remove hidden class');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dom: getByClassAll: without element uses document', (t) => {
|
||||||
|
const getElementsByClassName = stub();
|
||||||
|
const prevDocument = globalThis.document;
|
||||||
|
|
||||||
|
globalThis.document = {
|
||||||
|
getElementsByClassName,
|
||||||
|
};
|
||||||
|
|
||||||
|
getByClassAll('my-class');
|
||||||
|
|
||||||
|
globalThis.document = prevDocument;
|
||||||
|
|
||||||
|
t.calledWith(getElementsByClassName, ['my-class'], 'should fallback to document when no element');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -442,11 +442,7 @@ export function getFilenames(files) {
|
||||||
if (name === '..')
|
if (name === '..')
|
||||||
allFiles.shift();
|
allFiles.shift();
|
||||||
|
|
||||||
const names = allFiles.map((current) => {
|
return allFiles.map(DOM.getCurrentName);
|
||||||
return DOM.getCurrentName(current);
|
|
||||||
});
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,19 @@ export const loadRemote = callbackify(async (name, options) => {
|
||||||
local = [module.local];
|
local = [module.local];
|
||||||
}
|
}
|
||||||
|
|
||||||
const localURL = local.map((url) => prefix + url);
|
const localURL = [];
|
||||||
|
|
||||||
const remoteURL = remoteTmpls.map((tmpl) => {
|
for (const url of local) {
|
||||||
return rendy(tmpl, {
|
localURL.push(prefix + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteURL = [];
|
||||||
|
|
||||||
|
for (const tmpl of remoteTmpls) {
|
||||||
|
remoteURL.push(rendy(tmpl, {
|
||||||
version,
|
version,
|
||||||
});
|
}));
|
||||||
});
|
}
|
||||||
|
|
||||||
if (online) {
|
if (online) {
|
||||||
const [e] = await tryToCatch(load.parallel, remoteURL);
|
const [e] = await tryToCatch(load.parallel, remoteURL);
|
||||||
|
|
|
||||||
28
client/key/binder.spec.js
Normal file
28
client/key/binder.spec.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {test} from 'supertape';
|
||||||
|
import {createBinder} from './binder.js';
|
||||||
|
|
||||||
|
test('client: key: binder: isBind: default', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
t.notOk(binder.isBind(), 'should not be bind by default');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: key: binder: setBind', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
binder.setBind();
|
||||||
|
|
||||||
|
t.ok(binder.isBind(), 'should be bind');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('client: key: binder: unsetBind', (t) => {
|
||||||
|
const binder = createBinder();
|
||||||
|
|
||||||
|
binder.setBind();
|
||||||
|
binder.unsetBind();
|
||||||
|
|
||||||
|
t.notOk(binder.isBind(), 'should not be bind');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {getDOM} from './globals.fixture.js';
|
import {getDOM} from './globals.fixture.js';
|
||||||
import {_next, _previous} from './find.js';
|
import {
|
||||||
|
_next,
|
||||||
|
_previous,
|
||||||
|
find,
|
||||||
|
findNext,
|
||||||
|
findPrevious,
|
||||||
|
} from './find.js';
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
|
|
||||||
|
|
@ -11,9 +17,39 @@ test('cloudcmd: client: vim: _next', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: _next: increment', (t) => {
|
||||||
|
const result = _next(0, 2);
|
||||||
|
|
||||||
|
t.equal(result, 1, 'should return 1');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _previous', (t) => {
|
test('cloudcmd: client: vim: _previous', (t) => {
|
||||||
const result = _previous(0, 2);
|
const result = _previous(0, 2);
|
||||||
|
|
||||||
t.equal(result, 1, 'should return 1');
|
t.equal(result, 1, 'should return 1');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: _previous: decrement', (t) => {
|
||||||
|
const result = _previous(1, 2);
|
||||||
|
|
||||||
|
t.equal(result, 0, 'should return 0');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: findNext: after find', (t) => {
|
||||||
|
find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
const result = findNext();
|
||||||
|
|
||||||
|
t.equal(result, 'beta', 'should return next found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: vim: findPrevious: after find', (t) => {
|
||||||
|
find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
const result = findPrevious();
|
||||||
|
|
||||||
|
t.equal(result, 'apple', 'should return previous found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import {getDOM, getCloudCmd} from './globals.fixture.js';
|
import {getDOM, getCloudCmd} from './globals.fixture.js';
|
||||||
import vim, {selectFile as vimSelectFile} from './index.js';
|
import vim, {selectFile as vimSelectFile} from './index.js';
|
||||||
|
import * as finder from './find.js';
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
globalThis.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
globalThis.CloudCmd = getCloudCmd();
|
||||||
|
|
@ -675,3 +676,81 @@ test('cloudcmd: client: vim: rename', async (t) => {
|
||||||
t.calledWithNoArgs(renameCurrent);
|
t.calledWithNoArgs(renameCurrent);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: cc: operationCopy', (t) => {
|
||||||
|
const show = stub();
|
||||||
|
const preventDefault = stub();
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('c', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
vim('c', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(show, ['copy'], 'should show copy operation');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: mm: operationMove', (t) => {
|
||||||
|
const show = stub();
|
||||||
|
const preventDefault = stub();
|
||||||
|
|
||||||
|
const Operation = {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
preventDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
vim('m', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
vim('m', event, {
|
||||||
|
Operation,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(show, ['move'], 'should show move operation');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: n: findNext: real', (t) => {
|
||||||
|
const setCurrentByName = stub();
|
||||||
|
|
||||||
|
finder.find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('n', event, {
|
||||||
|
setCurrentByName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentByName, ['beta'], 'should set current by next found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: key: N: findPrevious: real', (t) => {
|
||||||
|
const setCurrentByName = stub();
|
||||||
|
|
||||||
|
finder.find('a', ['alpha', 'beta', 'apple']);
|
||||||
|
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('N', event, {
|
||||||
|
setCurrentByName,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(setCurrentByName, ['apple'], 'should set current by previous found name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import {test} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import {convert} from './input.js';
|
import {
|
||||||
|
convert,
|
||||||
|
getName,
|
||||||
|
getValue,
|
||||||
|
setValue,
|
||||||
|
} from './input.js';
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: convert', (t) => {
|
test('cloudcmd: client: config: input: convert', (t) => {
|
||||||
const result = convert({
|
const result = convert({
|
||||||
|
|
@ -13,3 +18,123 @@ test('cloudcmd: client: config: input: convert', (t) => {
|
||||||
t.deepEqual(result, expected);
|
t.deepEqual(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: convert: bool', (t) => {
|
||||||
|
const result = convert({
|
||||||
|
auth: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
auth: ' checked',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: convert: bool false', (t) => {
|
||||||
|
const result = convert({
|
||||||
|
auth: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
auth: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
t.deepEqual(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getName', (t) => {
|
||||||
|
const getAttribute = stub().returns('js-hello');
|
||||||
|
const element = {
|
||||||
|
getAttribute,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getName(element);
|
||||||
|
|
||||||
|
t.equal(result, 'hello', 'should strip js- prefix');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: checkbox', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('auth', element);
|
||||||
|
|
||||||
|
t.ok(result, 'should return checked value');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: number', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'number',
|
||||||
|
value: '42',
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('port', element);
|
||||||
|
|
||||||
|
t.equal(result, 42, 'should return number');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: getValue: default', (t) => {
|
||||||
|
const querySelector = stub().returns({
|
||||||
|
type: 'text',
|
||||||
|
value: 'hello',
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getValue('name', element);
|
||||||
|
|
||||||
|
t.equal(result, 'hello', 'should return value as is');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: setValue: checkbox', (t) => {
|
||||||
|
const el = {
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const querySelector = stub().returns(el);
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
setValue('auth', true, element);
|
||||||
|
|
||||||
|
t.ok(el.checked, 'should set checked');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: client: config: input: setValue: default', (t) => {
|
||||||
|
const el = {
|
||||||
|
type: 'text',
|
||||||
|
value: 'old',
|
||||||
|
};
|
||||||
|
|
||||||
|
const querySelector = stub().returns(el);
|
||||||
|
const element = {
|
||||||
|
querySelector,
|
||||||
|
};
|
||||||
|
|
||||||
|
setValue('name', 'new', element);
|
||||||
|
|
||||||
|
t.equal(el.value, 'new', 'should set value');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,10 @@ test('cloudcmd: client: view: initConfig', (t) => {
|
||||||
|
|
||||||
test('cloudcmd: client: view: initConfig: no options', (t) => {
|
test('cloudcmd: client: view: initConfig: no options', (t) => {
|
||||||
const config = _initConfig();
|
const config = _initConfig();
|
||||||
|
const result = typeof config;
|
||||||
|
const expected = 'object';
|
||||||
|
|
||||||
t.equal(typeof config, 'object');
|
t.equal(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ test('sw: register: registerSW: http', async (t, {location, navigator}) => {
|
||||||
|
|
||||||
test('sw: register: registerSW: https self-signed', async (t, {location, navigator}) => {
|
test('sw: register: registerSW: https self-signed', async (t, {location, navigator}) => {
|
||||||
Object.assign(location, {
|
Object.assign(location, {
|
||||||
protocol: 'https',
|
protocol: 'https:',
|
||||||
hostname: 'self-signed.badssl.com',
|
hostname: 'self-signed.badssl.com',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,17 @@ import {readFileSync} from 'node:fs';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {montag} from 'montag';
|
import {montag} from 'montag';
|
||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
|
import {tryCatch} from 'try-catch';
|
||||||
import {
|
import {
|
||||||
_getSize,
|
_getSize,
|
||||||
getPathLink,
|
getPathLink,
|
||||||
buildFromJSON,
|
buildFromJSON,
|
||||||
_getDataName,
|
_getDataName,
|
||||||
dateFormatter,
|
dateFormatter,
|
||||||
|
formatMsg,
|
||||||
|
getTitle,
|
||||||
|
getDotDot,
|
||||||
|
getHeaderField,
|
||||||
} from '#common/cloudfunc';
|
} from '#common/cloudfunc';
|
||||||
|
|
||||||
const templatePath = new URL('../tmpl/fs', import.meta.url).pathname;
|
const templatePath = new URL('../tmpl/fs', import.meta.url).pathname;
|
||||||
|
|
@ -221,6 +226,97 @@ test('cloudfunc: _getDataName', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: formatMsg: name', (t) => {
|
||||||
|
const result = formatMsg('hello', 'world');
|
||||||
|
const expected = 'hello: ok("world")';
|
||||||
|
|
||||||
|
t.equal(result, expected, 'should format message with name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: formatMsg: no name', (t) => {
|
||||||
|
const result = formatMsg('hello');
|
||||||
|
const expected = 'hello: ok';
|
||||||
|
|
||||||
|
t.equal(result, expected, 'should format message without name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getTitle: no options', (t) => {
|
||||||
|
const result = getTitle();
|
||||||
|
|
||||||
|
t.ok(result, 'should return a title string even without options');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getTitle: with name', (t) => {
|
||||||
|
const result = getTitle({
|
||||||
|
name: 'MyName',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.match(result, 'MyName', 'should return title with name');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getHeaderField: sort not name', (t) => {
|
||||||
|
const result = getHeaderField('size', 'asc', 'name');
|
||||||
|
|
||||||
|
t.equal(result, 'name', 'should return plain name when sort does not match');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getHeaderField: sort name asc', (t) => {
|
||||||
|
const result = getHeaderField('name', 'asc', 'name');
|
||||||
|
|
||||||
|
t.equal(result, 'name', 'should return plain name when name asc');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getDotDot: root', (t) => {
|
||||||
|
const result = getDotDot('/');
|
||||||
|
|
||||||
|
t.equal(result, '/', 'should return / for root path');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getPathLink: /a/b/c', (t) => {
|
||||||
|
const {pathLink} = template;
|
||||||
|
const result = getPathLink('/a/b/c/', '', pathLink);
|
||||||
|
|
||||||
|
t.ok(result, 'should build path link for 3-segment path');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getPathLink: no url', (t) => {
|
||||||
|
const [error] = tryCatch(getPathLink);
|
||||||
|
|
||||||
|
t.equal(error.message, 'url could not be empty!', 'should throw when url is empty');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getPathLink: no template', (t) => {
|
||||||
|
const [error] = tryCatch(getPathLink, '/');
|
||||||
|
|
||||||
|
t.equal(error.message, 'template could not be empty!', 'should throw when template is empty');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getHeaderField: sort name desc', (t) => {
|
||||||
|
const result = getHeaderField('name', 'desc', 'name');
|
||||||
|
const expected = 'name↓';
|
||||||
|
|
||||||
|
t.equal(result, expected, 'should return name with down arrow');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudfunc: getDotDot: normal path', (t) => {
|
||||||
|
const result = getDotDot('/hello/world/');
|
||||||
|
const expected = '/hello';
|
||||||
|
|
||||||
|
t.equal(result, expected, 'should return parent directory');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('cloudfunc: buildFromJSON: formatDate', (t) => {
|
test('cloudfunc: buildFromJSON: formatDate', (t) => {
|
||||||
const data = {
|
const data = {
|
||||||
path: '/media/',
|
path: '/media/',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import {
|
||||||
getRegExp,
|
getRegExp,
|
||||||
escapeRegExp,
|
escapeRegExp,
|
||||||
getExt,
|
getExt,
|
||||||
|
time,
|
||||||
|
timeEnd,
|
||||||
} from '#common/util';
|
} from '#common/util';
|
||||||
|
|
||||||
test('getExt: no extension', (t) => {
|
test('getExt: no extension', (t) => {
|
||||||
|
|
@ -106,11 +108,31 @@ test('util: getRegExp: no', (t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('util: escapeRegExp: no str', (t) => {
|
test('util: escapeRegExp: no str', (t) => {
|
||||||
t.equal(escapeRegExp(1), 1);
|
const result = escapeRegExp(1);
|
||||||
|
const expected = 1;
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('util: escapeRegExp', (t) => {
|
test('util: escapeRegExp', (t) => {
|
||||||
t.equal(escapeRegExp('#hello'), '\\#hello');
|
const result = escapeRegExp('#hello');
|
||||||
|
const expected = '\\#hello';
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('util: time', (t) => {
|
||||||
|
const [error] = tryCatch(time, 'test');
|
||||||
|
|
||||||
|
t.notOk(error, 'should not throw');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('util: timeEnd', (t) => {
|
||||||
|
const [error] = tryCatch(timeEnd, 'test');
|
||||||
|
|
||||||
|
t.notOk(error, 'should not throw');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
19
package.json
19
package.json
|
|
@ -151,10 +151,10 @@
|
||||||
"yargs-parser": "^22.0.0"
|
"yargs-parser": "^22.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/code-frame": "^7.22.5",
|
"@babel/code-frame": "^8.0.0",
|
||||||
"@babel/core": "^7.22.5",
|
"@babel/core": "^8.0.1",
|
||||||
"@babel/plugin-transform-optional-chaining": "^7.21.0",
|
"@babel/plugin-transform-optional-chaining": "^8.0.1",
|
||||||
"@babel/preset-env": "^7.0.0",
|
"@babel/preset-env": "^8.0.2",
|
||||||
"@cloudcmd/clipboard": "^2.0.0",
|
"@cloudcmd/clipboard": "^2.0.0",
|
||||||
"@cloudcmd/create-element": "^2.0.0",
|
"@cloudcmd/create-element": "^2.0.0",
|
||||||
"@cloudcmd/modal": "^4.0.0",
|
"@cloudcmd/modal": "^4.0.0",
|
||||||
|
|
@ -162,6 +162,8 @@
|
||||||
"@cloudcmd/stub": "^5.0.0",
|
"@cloudcmd/stub": "^5.0.0",
|
||||||
"@iocmd/wait": "^2.1.0",
|
"@iocmd/wait": "^2.1.0",
|
||||||
"@putout/eslint-flat": "^4.0.0",
|
"@putout/eslint-flat": "^4.0.0",
|
||||||
|
"@rspack/cli": "^2.1.4",
|
||||||
|
"@rspack/core": "^2.1.4",
|
||||||
"@supertape/loader-css": "^1.0.0",
|
"@supertape/loader-css": "^1.0.0",
|
||||||
"@types/node-fetch": "^2.6.11",
|
"@types/node-fetch": "^2.6.11",
|
||||||
"auto-globals": "^4.0.0",
|
"auto-globals": "^4.0.0",
|
||||||
|
|
@ -171,7 +173,6 @@
|
||||||
"clean-css-loader": "^4.2.1",
|
"clean-css-loader": "^4.2.1",
|
||||||
"codegen.macro": "^4.0.0",
|
"codegen.macro": "^4.0.0",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
|
||||||
"css-modules-require-hook": "^4.2.3",
|
"css-modules-require-hook": "^4.2.3",
|
||||||
"cssnano-preset-default": "^8.0.1",
|
"cssnano-preset-default": "^8.0.1",
|
||||||
"domtokenlist-shim": "^1.2.0",
|
"domtokenlist-shim": "^1.2.0",
|
||||||
|
|
@ -190,7 +191,6 @@
|
||||||
"load.js": "^3.0.0",
|
"load.js": "^3.0.0",
|
||||||
"madrun": "^13.0.0",
|
"madrun": "^13.0.0",
|
||||||
"memfs": "^4.2.0",
|
"memfs": "^4.2.0",
|
||||||
"mini-css-extract-plugin": "^2.9.2",
|
|
||||||
"minor": "^1.2.2",
|
"minor": "^1.2.2",
|
||||||
"morgan": "^1.6.1",
|
"morgan": "^1.6.1",
|
||||||
"multi-rename": "^3.0.0",
|
"multi-rename": "^3.0.0",
|
||||||
|
|
@ -205,7 +205,7 @@
|
||||||
"request": "^2.76.0",
|
"request": "^2.76.0",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"scroll-into-view-if-needed": "^3.0.4",
|
"scroll-into-view-if-needed": "^3.0.4",
|
||||||
"serve-once": "^3.0.1",
|
"serve-once": "^4.0.1",
|
||||||
"smalltalk": "^5.0.0",
|
"smalltalk": "^5.0.0",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"superc8": "^12.6.0",
|
"superc8": "^12.6.0",
|
||||||
|
|
@ -215,10 +215,7 @@
|
||||||
"unionfs": "^4.0.0",
|
"unionfs": "^4.0.0",
|
||||||
"url-loader": "^4.0.0",
|
"url-loader": "^4.0.0",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"webpack": "^5.99.9",
|
"webpack-merge": "^6.0.1"
|
||||||
"webpack-cli": "^7.0.2",
|
|
||||||
"webpack-merge": "^6.0.1",
|
|
||||||
"webpackbar": "^7.0.0"
|
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"#css/": "./css/",
|
"#css/": "./css/",
|
||||||
|
|
|
||||||
10
rspack.config.js
Normal file
10
rspack.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {merge} from 'webpack-merge';
|
||||||
|
import * as htmlConfig from './.rspack/html.js';
|
||||||
|
import cssConfig from './.rspack/css.js';
|
||||||
|
import jsConfig from './.rspack/js.js';
|
||||||
|
|
||||||
|
export default merge([
|
||||||
|
jsConfig,
|
||||||
|
htmlConfig,
|
||||||
|
cssConfig,
|
||||||
|
]);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import path, {dirname} from 'node:path';
|
import path, {dirname} from 'node:path';
|
||||||
import {fileURLToPath} from 'node:url';
|
import {fileURLToPath} from 'node:url';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import cloudcmd, {
|
import cloudcmd, {
|
||||||
_isDev,
|
_isDev,
|
||||||
|
|
@ -10,6 +10,9 @@ import cloudcmd, {
|
||||||
_initAuth,
|
_initAuth,
|
||||||
_getIndexPath,
|
_getIndexPath,
|
||||||
} from '#server/cloudcmd';
|
} from '#server/cloudcmd';
|
||||||
|
import {connect} from '../test/before.js';
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|
@ -158,17 +161,20 @@ test('cloudcmd: auth: accept: no auth', (t) => {
|
||||||
|
|
||||||
test('cloudcmd: getIndexPath: production', (t) => {
|
test('cloudcmd: getIndexPath: production', (t) => {
|
||||||
const isDev = false;
|
const isDev = false;
|
||||||
const name = path.join(__dirname, '..', 'dist', 'index.html');
|
|
||||||
|
|
||||||
t.equal(_getIndexPath(isDev), name);
|
const result = _getIndexPath(isDev);
|
||||||
|
const expected = path.join(__dirname, '..', 'dist', 'index.html');
|
||||||
|
|
||||||
|
t.equal(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: getIndexPath: development', (t) => {
|
test('cloudcmd: getIndexPath: development', (t) => {
|
||||||
const isDev = true;
|
const isDev = true;
|
||||||
const name = path.join(__dirname, '..', 'dist-dev', 'index.html');
|
const result = _getIndexPath(isDev);
|
||||||
|
const expected = path.join(__dirname, '..', 'dist-dev', 'index.html');
|
||||||
|
|
||||||
t.equal(_getIndexPath(isDev), name);
|
t.equal(result, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -178,3 +184,64 @@ test('cloudcmd: sw', async (t) => {
|
||||||
t.equal(status, 200, 'should return sw');
|
t.equal(status, 200, 'should return sw');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: no params', (t) => {
|
||||||
|
const middle = cloudcmd();
|
||||||
|
|
||||||
|
t.ok(Array.isArray(middle), 'should return middleware list when no params passed');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: listen: terminal', async (t) => {
|
||||||
|
const {done} = await connect({
|
||||||
|
config: {
|
||||||
|
terminal: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await done();
|
||||||
|
|
||||||
|
t.pass('should listen with terminal enabled');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: middle: dropbox', async (t) => {
|
||||||
|
const {port, done} = await connect({
|
||||||
|
config: {
|
||||||
|
dropbox: true,
|
||||||
|
dropboxToken: 'hello',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(`http://localhost:${port}/api/v1/dropbox/nonexistent`);
|
||||||
|
|
||||||
|
await done();
|
||||||
|
|
||||||
|
t.ok(response.status, 'should mount dropbox route');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: logout', async (t) => {
|
||||||
|
const {status} = await request.get('/logout');
|
||||||
|
|
||||||
|
t.equal(status, 401, 'should return 401 for /logout');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: modules', (t) => {
|
||||||
|
const middle = cloudcmd({
|
||||||
|
modules: {
|
||||||
|
hello: noop,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(Array.isArray(middle), 'should return middleware list with modules');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: setUrl: cloudcmd.js', async (t) => {
|
||||||
|
const {status} = await request.get('/cloudcmd.js');
|
||||||
|
|
||||||
|
t.equal(status, 200, 'should serve cloudcmd.js');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,32 @@ test('distribute: log: config', (t) => {
|
||||||
}, {
|
}, {
|
||||||
checkAssertionsCount: false,
|
checkAssertionsCount: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('distribute: log: stringToRGB', (t) => {
|
||||||
|
const result = log.stringToRGB('abc');
|
||||||
|
|
||||||
|
t.deepEqual(result, [97, 3, 294], 'should return [charCode, length, crc]');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('distribute: log: makeColor', (t) => {
|
||||||
|
const result = log.makeColor('hello');
|
||||||
|
|
||||||
|
t.match(result, 'hello', 'should return colored string containing the input');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('distribute: log: getDescription', (t) => {
|
||||||
|
const message = 'some error';
|
||||||
|
const result = log.getDescription({
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(result, message, 'should return message from error object');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('distribute: log: connectedStr', (t) => {
|
||||||
|
t.ok(log.connectedStr, 'should have connectedStr');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,16 @@ test('cloudcmd: server: env: bool: number: 0', (t) => {
|
||||||
t.notOk(result);
|
t.notOk(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: server: env: bool: zero uppercase', (t) => {
|
||||||
|
const {CLOUDCMD_TERMINAL} = process.env;
|
||||||
|
|
||||||
|
process.env.CLOUDCMD_TERMINAL = '0';
|
||||||
|
|
||||||
|
const result = env.bool('terminal');
|
||||||
|
|
||||||
|
process.env.CLOUDCMD_TERMINAL = CLOUDCMD_TERMINAL;
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,44 +4,46 @@ import exit from './exit.js';
|
||||||
|
|
||||||
test('cloudcmd: exit: process.exit', (t) => {
|
test('cloudcmd: exit: process.exit', (t) => {
|
||||||
const {exit: exitOriginal} = process;
|
const {exit: exitOriginal} = process;
|
||||||
|
const exitStub = stub();
|
||||||
|
|
||||||
process.exit = stub();
|
process.exit = exitStub;
|
||||||
|
|
||||||
exit();
|
exit();
|
||||||
t.calledWith(process.exit, [1], 'should call process.exit');
|
|
||||||
process.exit = exitOriginal;
|
process.exit = exitOriginal;
|
||||||
|
|
||||||
|
t.calledWith(exitStub, [1], 'should call process.exit');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: exit: console.error', (t) => {
|
test('cloudcmd: exit: console.error', (t) => {
|
||||||
const {exit: exitOriginal} = process;
|
const {exit: exitOriginal} = process;
|
||||||
const {error} = console;
|
const {error} = console;
|
||||||
|
const errorStub = stub();
|
||||||
|
|
||||||
console.error = stub();
|
console.error = errorStub;
|
||||||
process.exit = stub();
|
process.exit = stub();
|
||||||
|
|
||||||
exit('hello world');
|
exit('hello world');
|
||||||
t.calledWith(console.error, ['hello world'], 'should call console.error');
|
|
||||||
|
|
||||||
process.exit = exitOriginal;
|
process.exit = exitOriginal;
|
||||||
console.error = error;
|
console.error = error;
|
||||||
|
|
||||||
|
t.calledWith(errorStub, ['hello world'], 'should call console.error');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: exit.error: console.error: error', (t) => {
|
test('cloudcmd: exit.error: console.error: error', (t) => {
|
||||||
const {exit: exitOriginal} = process;
|
const {exit: exitOriginal} = process;
|
||||||
const {error} = console;
|
const {error} = console;
|
||||||
|
const errorStub = stub();
|
||||||
|
|
||||||
console.error = stub();
|
console.error = errorStub;
|
||||||
process.exit = stub();
|
process.exit = stub();
|
||||||
|
|
||||||
exit(Error('hello world'));
|
exit(Error('hello world'));
|
||||||
t.calledWith(console.error, ['hello world'], 'should call console.error');
|
|
||||||
|
|
||||||
process.exit = exitOriginal;
|
process.exit = exitOriginal;
|
||||||
console.error = error;
|
console.error = error;
|
||||||
|
|
||||||
|
t.calledWith(errorStub, ['hello world'], 'should call console.error');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {join} from 'node:path';
|
||||||
import {promisify} from 'node:util';
|
import {promisify} from 'node:util';
|
||||||
import {tryToCatch} from 'try-to-catch';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
import markdown from './index.js';
|
import markdown from './index.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ const UserError = (msg) => {
|
||||||
return error;
|
return error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const _UserError = UserError;
|
||||||
|
|
||||||
export default currify(({config, fs = _fs, moveFiles = _moveFiles}, request, response, next) => {
|
export default currify(({config, fs = _fs, moveFiles = _moveFiles}, request, response, next) => {
|
||||||
const name = ponse.getPathName(request);
|
const name = ponse.getPathName(request);
|
||||||
const regExp = RegExp(`^${apiURL}`);
|
const regExp = RegExp(`^${apiURL}`);
|
||||||
|
|
@ -153,6 +155,8 @@ function getPackReg(packer) {
|
||||||
return /\.tar\.gz$/;
|
return /\.tar\.gz$/;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _getPackReg = getPackReg;
|
||||||
|
|
||||||
function streamPack(cmd, response, packer) {
|
function streamPack(cmd, response, packer) {
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
const filename = cmd.replace(getPackReg(packer), '');
|
const filename = cmd.replace(getPackReg(packer), '');
|
||||||
|
|
@ -172,6 +176,8 @@ function getCMD(cmd) {
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _getCMD = getCMD;
|
||||||
|
|
||||||
const getMoveMsg = (names) => formatMsg('move', names);
|
const getMoveMsg = (names) => formatMsg('move', names);
|
||||||
|
|
||||||
const getRenameMsg = (from, to) => {
|
const getRenameMsg = (from, to) => {
|
||||||
|
|
@ -276,6 +282,8 @@ function rename(rootDir, from, to, fs, callback) {
|
||||||
return fs.rename(fromRooted, toRooted, fn);
|
return fs.rename(fromRooted, toRooted, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _rename = rename;
|
||||||
|
|
||||||
function pack(from, to, names, config, fn) {
|
function pack(from, to, names, config, fn) {
|
||||||
const rootDir = config('root');
|
const rootDir = config('root');
|
||||||
const packer = config('packer');
|
const packer = config('packer');
|
||||||
|
|
@ -294,6 +302,8 @@ function pack(from, to, names, config, fn) {
|
||||||
operation('pack', packer, from, to, names, fn);
|
operation('pack', packer, from, to, names, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _pack = pack;
|
||||||
|
|
||||||
function extract(from, to, config, fn) {
|
function extract(from, to, config, fn) {
|
||||||
const rootDir = config('root');
|
const rootDir = config('root');
|
||||||
|
|
||||||
|
|
@ -307,6 +317,8 @@ function extract(from, to, config, fn) {
|
||||||
operation('extract', config('packer'), from, to, fn);
|
operation('extract', config('packer'), from, to, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _extract = extract;
|
||||||
|
|
||||||
function getPacker(operation, packer) {
|
function getPacker(operation, packer) {
|
||||||
if (operation === 'extract')
|
if (operation === 'extract')
|
||||||
return inly;
|
return inly;
|
||||||
|
|
@ -317,6 +329,8 @@ function getPacker(operation, packer) {
|
||||||
return jaguar.pack;
|
return jaguar.pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _getPacker = getPacker;
|
||||||
|
|
||||||
function operation(op, packer, from, to, names, fn) {
|
function operation(op, packer, from, to, names, fn) {
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
fn = names;
|
fn = names;
|
||||||
|
|
@ -345,6 +359,8 @@ function operation(op, packer, from, to, names, fn) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _operation = operation;
|
||||||
|
|
||||||
function copy(from, to, names, fn) {
|
function copy(from, to, names, fn) {
|
||||||
copymitter(from, to, names)
|
copymitter(from, to, names)
|
||||||
.on('error', fn)
|
.on('error', fn)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {test} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import {tryToCatch} from 'try-to-catch';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
import {
|
import {
|
||||||
_formatMsg,
|
_formatMsg,
|
||||||
|
|
@ -6,6 +6,11 @@ import {
|
||||||
_isRootWin32,
|
_isRootWin32,
|
||||||
_isRootAll,
|
_isRootAll,
|
||||||
_onPUT,
|
_onPUT,
|
||||||
|
_UserError,
|
||||||
|
_getPackReg,
|
||||||
|
_getCMD,
|
||||||
|
_rename,
|
||||||
|
_getPacker,
|
||||||
} from './index.js';
|
} from './index.js';
|
||||||
|
|
||||||
test('rest: formatMsg', (t) => {
|
test('rest: formatMsg', (t) => {
|
||||||
|
|
@ -70,3 +75,104 @@ test('rest: onPUT: no callback', async (t) => {
|
||||||
t.equal(e.message, 'callback should be a function!', 'should throw when no callback');
|
t.equal(e.message, 'callback should be a function!', 'should throw when no callback');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('rest: UserError: message', (t) => {
|
||||||
|
const result = _UserError('hello');
|
||||||
|
|
||||||
|
t.equal(result.message, 'hello', 'should set message');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: UserError: code', (t) => {
|
||||||
|
const result = _UserError('hello');
|
||||||
|
|
||||||
|
t.equal(result.code, 'EUSER', 'should set code');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getPackReg: zip', (t) => {
|
||||||
|
const result = _getPackReg('zip');
|
||||||
|
|
||||||
|
t.match('file.zip', result, 'should match .zip');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getPackReg: tar', (t) => {
|
||||||
|
const result = _getPackReg('tar');
|
||||||
|
|
||||||
|
t.match('file.tar.gz', result, 'should match .tar.gz');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getCMD: with slash', (t) => {
|
||||||
|
const result = _getCMD('/move');
|
||||||
|
|
||||||
|
t.equal(result, 'move', 'should strip leading slash');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getCMD: no slash', (t) => {
|
||||||
|
const result = _getCMD('move');
|
||||||
|
|
||||||
|
t.equal(result, 'move', 'should return as is');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: rename: no from', (t) => {
|
||||||
|
const callback = stub();
|
||||||
|
|
||||||
|
_rename('/', null, 'to', null, callback);
|
||||||
|
|
||||||
|
const msg = '"from" should be filled';
|
||||||
|
|
||||||
|
t.calledWith(callback, [
|
||||||
|
_UserError(msg),
|
||||||
|
], 'should return UserError');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: rename: no to', (t) => {
|
||||||
|
const callback = stub();
|
||||||
|
|
||||||
|
_rename('/', 'from', null, null, callback);
|
||||||
|
|
||||||
|
const msg = '"to" should be filled';
|
||||||
|
|
||||||
|
t.calledWith(callback, [
|
||||||
|
_UserError(msg),
|
||||||
|
], 'should return UserError');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: rename: success', (t) => {
|
||||||
|
const callback = stub();
|
||||||
|
const fs = {
|
||||||
|
rename: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_rename('/root', 'from', 'to', fs, callback);
|
||||||
|
|
||||||
|
t.calledWith(fs.rename, ['/root/from', '/root/to', callback], 'should call fs.rename');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getPacker: extract', (t) => {
|
||||||
|
const result = _getPacker('extract', 'zip');
|
||||||
|
|
||||||
|
t.equal(typeof result, 'function', 'should return function');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getPacker: pack zip', (t) => {
|
||||||
|
const result = _getPacker('pack', 'zip');
|
||||||
|
|
||||||
|
t.equal(typeof result, 'function', 'should return function');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rest: getPacker: pack tar', (t) => {
|
||||||
|
const result = _getPacker('pack', 'tar');
|
||||||
|
|
||||||
|
t.equal(typeof result, 'function', 'should return function');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {Readable} from 'node:stream';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import {tryToCatch} from 'try-to-catch';
|
import {tryToCatch} from 'try-to-catch';
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
import {_getReadDir} from './route.js';
|
import {_getReadDir} from './route.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,3 +30,21 @@ test('themes: no args', (t) => {
|
||||||
t.match(themes.light, css);
|
t.match(themes.light, css);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('themes: production', (t) => {
|
||||||
|
const themes = getThemes({
|
||||||
|
isDev: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(themes.dark, 'should have dark theme');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('themes: production: light', (t) => {
|
||||||
|
const themes = getThemes({
|
||||||
|
isDev: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(themes.light, 'should have light theme');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import {dirname, join} from 'node:path';
|
||||||
import {fileURLToPath} from 'node:url';
|
import {fileURLToPath} from 'node:url';
|
||||||
import {readFileSync} from 'node:fs';
|
import {readFileSync} from 'node:fs';
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {putout} from 'putout';
|
import {putout} from 'putout';
|
||||||
import {userMenu} from './user-menu.js';
|
import {userMenu} from './user-menu.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,3 +131,26 @@ test('validate: theme: wrong', (t) => {
|
||||||
t.calledWith(exit, [msg], 'should call exit');
|
t.calledWith(exit, [msg], 'should call exit');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('validate: menu: not valid', (t) => {
|
||||||
|
const exit = stub();
|
||||||
|
const msg = 'cloudcmd --menu: could be "supermenu" or "aleman" only';
|
||||||
|
|
||||||
|
validate.menu('hello', {
|
||||||
|
exit,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.calledWith(exit, [msg], 'should call fn');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate: menu: valid', (t) => {
|
||||||
|
const exit = stub();
|
||||||
|
|
||||||
|
validate.menu('supermenu', {
|
||||||
|
exit,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.notCalled(exit, 'should not call fn');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
|
import {spawn} from 'node:child_process';
|
||||||
import {test} from 'supertape';
|
import {test} from 'supertape';
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
test('cloudcmd: server: ratelimit: x-forwarded-for', async (t) => {
|
test('cloudcmd: server: ratelimit: x-forwarded-for', async (t) => {
|
||||||
const PORT = 3000;
|
const PORT = 3000;
|
||||||
|
const child = await run(PORT);
|
||||||
process.env.PORT = PORT;
|
|
||||||
process.env.CLOUDCMD_LOG = 0;
|
|
||||||
|
|
||||||
await import('../bin/cloudcmd.js');
|
|
||||||
|
|
||||||
const {status} = await fetch(`http://localhost:${PORT}`, {
|
const {status} = await fetch(`http://localhost:${PORT}`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -17,7 +12,7 @@ test('cloudcmd: server: ratelimit: x-forwarded-for', async (t) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
process.kill(process.pid, 'SIGUSR1');
|
child.kill();
|
||||||
|
|
||||||
t.notEqual(status, 500);
|
t.notEqual(status, 500);
|
||||||
t.end();
|
t.end();
|
||||||
|
|
@ -27,18 +22,34 @@ test('cloudcmd: server: ratelimit', async (t) => {
|
||||||
const PORT = 3001;
|
const PORT = 3001;
|
||||||
const STATUS = 429;
|
const STATUS = 429;
|
||||||
|
|
||||||
process.env.PORT = PORT;
|
const child = await run(PORT);
|
||||||
process.env.CLOUDCMD_LOG = 0;
|
|
||||||
|
|
||||||
await import(`../bin/cloudcmd.js?${i++}`);
|
|
||||||
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
for (let i = 0; i < 1000; i++) {
|
||||||
await fetch(`http://localhost:${PORT}`);
|
await fetch(`http://localhost:${PORT}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {status} = await fetch(`http://localhost:${PORT}`);
|
const {status} = await fetch(`http://localhost:${PORT}`);
|
||||||
process.kill(process.pid, 'SIGUSR1');
|
child.kill();
|
||||||
|
|
||||||
t.equal(status, STATUS);
|
t.equal(status, STATUS);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function run(port) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const child = spawn(new URL('../bin/cloudcmd.js', import.meta.url).pathname, [], {
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
PORT: port,
|
||||||
|
},
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stdout.on('data', (a) => {
|
||||||
|
if (a.toString().includes('url'))
|
||||||
|
resolve(child);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ function before(options, fn = options) {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.listen(() => {
|
server.listen(() => {
|
||||||
fn(server
|
fn(server.address().port, promisify(after));
|
||||||
.address().port, promisify(after));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {dirname, join} from 'node:path';
|
import {dirname, join} from 'node:path';
|
||||||
import {fileURLToPath} from 'node:url';
|
import {fileURLToPath} from 'node:url';
|
||||||
import {mkdirSync} from 'node:fs';
|
import {mkdirSync} from 'node:fs';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {rimraf} from 'rimraf';
|
import {rimraf} from 'rimraf';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {EventEmitter} from 'node:events';
|
import {EventEmitter} from 'node:events';
|
||||||
import wait from '@iocmd/wait';
|
import wait from '@iocmd/wait';
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
||||||
test('cloudcmd: rest: move', async (t) => {
|
test('cloudcmd: rest: move', async (t) => {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import test from 'supertape';
|
||||||
import tar from 'tar-stream';
|
import tar from 'tar-stream';
|
||||||
import gunzip from 'gunzip-maybe';
|
import gunzip from 'gunzip-maybe';
|
||||||
import pullout from 'pullout';
|
import pullout from 'pullout';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import fs from 'node:fs';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import {Volume} from 'memfs';
|
import {Volume} from 'memfs';
|
||||||
import {ufs} from 'unionfs';
|
import {ufs} from 'unionfs';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
||||||
test('cloudcmd: rest: rename', async (t) => {
|
test('cloudcmd: rest: rename', async (t) => {
|
||||||
|
|
|
||||||
|
|
@ -4,34 +4,37 @@ import * as env from '../../server/env.js';
|
||||||
|
|
||||||
test('env: small', (t) => {
|
test('env: small', (t) => {
|
||||||
process.env.cloudcmd_hello = 'world';
|
process.env.cloudcmd_hello = 'world';
|
||||||
t.equal(env.parse('hello'), 'world', 'should parse string from env');
|
|
||||||
|
|
||||||
delete process.env.cloudcmd_hello;
|
delete process.env.cloudcmd_hello;
|
||||||
|
|
||||||
|
t.equal(env.parse('hello'), 'world', 'should parse string from env');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('env: big', (t) => {
|
test('env: big', (t) => {
|
||||||
process.env.CLOUDCMD_HELLO = 'world';
|
process.env.CLOUDCMD_HELLO = 'world';
|
||||||
t.equal(env.parse('hello'), 'world', 'should parse string from env');
|
|
||||||
|
|
||||||
delete process.env.CLOUDCMD_HELLO;
|
delete process.env.CLOUDCMD_HELLO;
|
||||||
|
|
||||||
|
t.equal(env.parse('hello'), 'world', 'should parse string from env');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('env: bool: false', (t) => {
|
test('env: bool: false', (t) => {
|
||||||
process.env.cloudcmd_terminal = 'false';
|
process.env.cloudcmd_terminal = 'false';
|
||||||
t.notOk(env.bool('terminal'), 'should return false');
|
|
||||||
|
|
||||||
delete process.env.cloudcmd_terminal;
|
delete process.env.cloudcmd_terminal;
|
||||||
|
|
||||||
|
t.notOk(env.bool('terminal'), 'should return false');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('env: bool: true', (t) => {
|
test('env: bool: true', (t) => {
|
||||||
process.env.cloudcmd_terminal = 'true';
|
process.env.cloudcmd_terminal = 'true';
|
||||||
|
|
||||||
t.ok(env.bool('terminal'), 'should be true');
|
|
||||||
|
|
||||||
delete process.env.cloudcmd_terminal;
|
delete process.env.cloudcmd_terminal;
|
||||||
|
|
||||||
|
t.ok(env.bool('terminal'), 'should be true');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -39,9 +42,10 @@ test('env: bool: undefined', (t) => {
|
||||||
const {cloudcmd_terminal} = process.env;
|
const {cloudcmd_terminal} = process.env;
|
||||||
|
|
||||||
process.env.cloudcmd_terminal = undefined;
|
process.env.cloudcmd_terminal = undefined;
|
||||||
|
|
||||||
t.notOk(env.bool('terminal'), 'should be undefined');
|
|
||||||
|
|
||||||
process.env.cloudcmd_terminal = cloudcmd_terminal;
|
process.env.cloudcmd_terminal = cloudcmd_terminal;
|
||||||
|
|
||||||
|
const result = env.bool('terminal');
|
||||||
|
|
||||||
|
t.notOk(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {createRequire} from 'node:module';
|
import {createRequire} from 'node:module';
|
||||||
import {dirname, join} from 'node:path';
|
import {dirname, join} from 'node:path';
|
||||||
import {fileURLToPath} from 'node:url';
|
import {fileURLToPath} from 'node:url';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import {test, stub} from 'supertape';
|
import {test, stub} from 'supertape';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
import modulas from '../../server/modulas.js';
|
import modulas from '../../server/modulas.js';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import {Buffer} from 'node:buffer';
|
import {Buffer} from 'node:buffer';
|
||||||
import serveOnce from 'serve-once';
|
import {serveOnce} from 'serve-once';
|
||||||
import test from 'supertape';
|
import test from 'supertape';
|
||||||
import criton from 'criton';
|
import criton from 'criton';
|
||||||
import {cloudcmd} from '#server/cloudcmd';
|
import {cloudcmd} from '#server/cloudcmd';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue