refactor: testing
This commit is contained in:
parent
2a98363942
commit
27e3275a39
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
|
@ -8,15 +8,58 @@ on:
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
|
||||||
uses: ./.github/workflows/node.yml
|
|
||||||
with:
|
|
||||||
run: npm run lint
|
|
||||||
test:
|
|
||||||
uses: ./.github/workflows/node.yml
|
|
||||||
with:
|
|
||||||
run: npm run -- test -p 360000
|
|
||||||
build:
|
build:
|
||||||
uses: ./.github/workflows/node.yml
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
run: npm run build
|
node-version: 20.x
|
||||||
|
cache: 'npm'
|
||||||
|
- run: npm config set registry ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
if: ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
- run: |
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
max-parallel: ${{ vars.CI_PARALLEL || 256 }}
|
||||||
|
matrix:
|
||||||
|
node-version: [16.x, 18.x, 20.x]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgconf-2-4 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm-dev libnss3-dev libxss-dev libasound2
|
||||||
|
- uses: browser-actions/setup-chrome@latest
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
- run: npm config set registry ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
if: ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
- run: |
|
||||||
|
npm ci
|
||||||
|
npm run -- test -t 360000
|
||||||
|
npm run -- test -t 360000 -p e2e
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
cache: 'npm'
|
||||||
|
- run: npm config set registry ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
if: ${{ vars.NPM_CI_REGISTRY }}
|
||||||
|
- run: |
|
||||||
|
npm ci
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
|
12
.github/workflows/e2e.yml
vendored
12
.github/workflows/e2e.yml
vendored
|
@ -1,12 +0,0 @@
|
||||||
name: End-to-end tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '20 4 * * *'
|
|
||||||
workflow_dispatch: {}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
e2e:
|
|
||||||
uses: ./.github/workflows/node.yml
|
|
||||||
with:
|
|
||||||
run: npm run -- test -t 360000 -p e2e
|
|
27
.github/workflows/node.yml
vendored
27
.github/workflows/node.yml
vendored
|
@ -1,27 +0,0 @@
|
||||||
name: Continuous Integration
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
run:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
node:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [18.x, 20.x]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
cache: 'npm'
|
|
||||||
- run: npm config set registry ${{ vars.NPM_CI_REGISTRY }}
|
|
||||||
if: ${{ vars.NPM_CI_REGISTRY }}
|
|
||||||
- run: |
|
|
||||||
npm ci
|
|
||||||
${{ inputs.run }}
|
|
811
package-lock.json
generated
811
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -261,11 +261,12 @@ exports.commands = (program, flecks) => {
|
||||||
const spawnWebpack = () => {
|
const spawnWebpack = () => {
|
||||||
webpack = spawnWith(cmd, options);
|
webpack = spawnWith(cmd, options);
|
||||||
webpack.on('message', (message) => {
|
webpack.on('message', (message) => {
|
||||||
switch (message) {
|
switch (message.type) {
|
||||||
case 'kill':
|
case 'kill':
|
||||||
debug('killing...');
|
debug('killing...');
|
||||||
webpack.kill();
|
webpack.kill();
|
||||||
watcher.close();
|
watcher.close();
|
||||||
|
process.exitCode = message.payload;
|
||||||
break;
|
break;
|
||||||
case 'restart':
|
case 'restart':
|
||||||
debug('restarting webpack...');
|
debug('restarting webpack...');
|
||||||
|
|
|
@ -41,6 +41,7 @@ module.exports = async (flecks) => ({
|
||||||
'test/**/*.js',
|
'test/**/*.js',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
'prefer-arrow-callback': ['error', {allowNamedFunctions: true}],
|
||||||
'brace-style': 'off',
|
'brace-style': 'off',
|
||||||
'class-methods-use-this': 'off',
|
'class-methods-use-this': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
|
|
@ -26,9 +26,11 @@ exports.hooks = {
|
||||||
}
|
}
|
||||||
config.plugins.push(new ProcessAssets(target, flecks));
|
config.plugins.push(new ProcessAssets(target, flecks));
|
||||||
},
|
},
|
||||||
'@flecks/build.config.alter': async ({test}) => {
|
'@flecks/build.config.alter': async ({test}, env, argv, flecks) => {
|
||||||
if (test) {
|
if (test) {
|
||||||
|
// Externalize the rest.
|
||||||
test.externals = await externals({
|
test.externals = await externals({
|
||||||
|
additionalModuleDirs: flecks.resolver.modules,
|
||||||
allowlist: Object.keys(test.resolve.fallback).map((fallback) => new RegExp(fallback)),
|
allowlist: Object.keys(test.resolve.fallback).map((fallback) => new RegExp(fallback)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {randomBytes} from 'crypto';
|
import {randomBytes} from 'crypto';
|
||||||
import {mkdir} from 'fs/promises';
|
import {mkdir} from 'fs/promises';
|
||||||
import {tmpdir} from 'os';
|
import {tmpdir} from 'os';
|
||||||
import {join} from 'path';
|
import {basename, join} from 'path';
|
||||||
|
|
||||||
import {rimraf} from 'rimraf';
|
import {rimraf} from 'rimraf';
|
||||||
|
|
||||||
|
@ -12,8 +12,23 @@ export function id() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createWorkspace() {
|
export async function createWorkspace() {
|
||||||
const workspace = join(tmpdir(), '@flecks', 'core', 'testing', await id());
|
let workspace = join(tmpdir(), '@flecks', 'core', 'testing', await id());
|
||||||
|
try {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
workspace += `-${basename(
|
||||||
|
error.stack
|
||||||
|
.split('\n').slice(-1)[0]
|
||||||
|
.split('at ')[1]
|
||||||
|
.match(/\((.*)\)$/)[1]
|
||||||
|
.split(':').slice(-3, -2)[0],
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
await mkdir(workspace, {recursive: true});
|
await mkdir(workspace, {recursive: true});
|
||||||
|
process.on('exit', () => {
|
||||||
|
rimraf.sync(workspace);
|
||||||
|
});
|
||||||
// sheeeeesh
|
// sheeeeesh
|
||||||
process.prependListener('message', async (message) => {
|
process.prependListener('message', async (message) => {
|
||||||
if ('__workerpool-terminate__' === message) {
|
if ('__workerpool-terminate__' === message) {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const {access} = require('fs/promises');
|
const {access} = require('fs/promises');
|
||||||
const {join, relative} = require('path');
|
const {join} = require('path');
|
||||||
|
|
||||||
const {commands: coreCommands} = require('@flecks/build/build/commands');
|
const {commands: coreCommands} = require('@flecks/build/build/commands');
|
||||||
const {rimraf} = require('@flecks/build/src/server');
|
const {rimraf} = require('@flecks/build/src/server');
|
||||||
const {D} = require('@flecks/core/src');
|
const {D} = require('@flecks/core/src');
|
||||||
const {glob} = require('@flecks/core/src/server');
|
const {glob, pipesink, processCode} = require('@flecks/core/src/server');
|
||||||
const Mocha = require('mocha');
|
const Mocha = require('mocha');
|
||||||
const {watchParallelRun} = require('mocha/lib/cli/watch-run');
|
const {watchParallelRun} = require('mocha/lib/cli/watch-run');
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const debug = D('@flecks/build.commands');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
TERM,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
module.exports = (program, flecks) => {
|
module.exports = (program, flecks) => {
|
||||||
|
@ -41,47 +42,36 @@ module.exports = (program, flecks) => {
|
||||||
watch,
|
watch,
|
||||||
} = opts;
|
} = opts;
|
||||||
const {build} = coreCommands(program, flecks);
|
const {build} = coreCommands(program, flecks);
|
||||||
let files = [];
|
// Remove the previous test(s).
|
||||||
if (platforms.includes('default')) {
|
|
||||||
files.push(...await glob(join(FLECKS_CORE_ROOT, 'test', '*.js')));
|
|
||||||
}
|
|
||||||
await Promise.all(
|
|
||||||
platforms
|
|
||||||
.filter((platform) => 'default' !== platform)
|
|
||||||
.map(async (platform) => {
|
|
||||||
files.push(...await glob(join(FLECKS_CORE_ROOT, 'test', platform, '*.js')));
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (0 === files.length) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('No tests found.');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
files = files.map((path) => relative(FLECKS_CORE_ROOT, path));
|
|
||||||
if (only) {
|
|
||||||
if (files.includes(only)) {
|
|
||||||
files = [only];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error(`Test '${only}' does not exist!`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files = files.map((file) => join('dist', file));
|
|
||||||
// Remove the previous test.
|
|
||||||
await rimraf(join(FLECKS_CORE_ROOT, 'dist', 'test'));
|
await rimraf(join(FLECKS_CORE_ROOT, 'dist', 'test'));
|
||||||
// Kick off building the test and wait for the file to exist.
|
// Kick off building the test and wait for the file to exist.
|
||||||
await build.action(
|
const child = await build.action(
|
||||||
'test',
|
'test',
|
||||||
{
|
{
|
||||||
env: {FLECKS_CORE_TEST_PLATFORMS: JSON.stringify(platforms)},
|
env: {
|
||||||
|
FLECKS_CORE_TEST_PLATFORMS: JSON.stringify(platforms),
|
||||||
|
FORCE_COLOR: 'dumb' !== TERM,
|
||||||
|
},
|
||||||
production,
|
production,
|
||||||
stdio: 'ignore',
|
stdio: watch ? 'inherit' : 'pipe',
|
||||||
watch,
|
watch,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (!watch) {
|
||||||
|
const stdout = pipesink(child.stdout);
|
||||||
|
if (0 !== await processCode(child)) {
|
||||||
|
const buffer = await stdout;
|
||||||
|
if (!process.stdout.write(buffer)) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
process.stdout.on('error', reject);
|
||||||
|
process.stdout.on('drain', resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
program.error('\nbuilding tests failed!\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
debug('Testing...', opts);
|
debug('Testing...', opts);
|
||||||
// eslint-disable-next-line no-constant-condition
|
while (watch) {
|
||||||
while (true) {
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await access(join(FLECKS_CORE_ROOT, 'dist', 'test'));
|
await access(join(FLECKS_CORE_ROOT, 'dist', 'test'));
|
||||||
|
@ -94,6 +84,19 @@ module.exports = (program, flecks) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let files = await glob(join(FLECKS_CORE_ROOT, 'dist', 'test', '**', '*.js'));
|
||||||
|
if (0 === files.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (only) {
|
||||||
|
const index = files.indexOf(join(FLECKS_CORE_ROOT, 'dist', only));
|
||||||
|
if (-1 !== index) {
|
||||||
|
files = [files[index]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error(`Test '${only}' does not exist!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Magic.
|
// Magic.
|
||||||
require('@flecks/core/build/resolve')(
|
require('@flecks/core/build/resolve')(
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,15 +92,17 @@ class StartServerPlugin {
|
||||||
...(inspectPort && {inspectPort}),
|
...(inspectPort && {inspectPort}),
|
||||||
});
|
});
|
||||||
this.worker = cluster.fork(env);
|
this.worker = cluster.fork(env);
|
||||||
|
this.worker.on('exit', (code) => {
|
||||||
|
if (killOnExit) {
|
||||||
|
process.send({type: 'kill', payload: code});
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.worker.on('disconnect', () => {
|
this.worker.on('disconnect', () => {
|
||||||
if (this.worker.exitedAfterDisconnect) {
|
if (this.worker.exitedAfterDisconnect) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('[HMR] Restarting application...');
|
console.error('[HMR] Restarting application...');
|
||||||
process.send('restart');
|
process.send({type: 'restart'});
|
||||||
}
|
|
||||||
else if (killOnExit) {
|
|
||||||
process.send('kill');
|
|
||||||
process.exit(0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"entry.js",
|
"entry.js",
|
||||||
"runtime.js"
|
"runtime.js",
|
||||||
|
"server.js"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flecks/core": "^4.0.5"
|
"@flecks/core": "^4.0.5"
|
||||||
|
|
|
@ -20,15 +20,9 @@ import {D, Flecks} from '@flecks/core';
|
||||||
}
|
}
|
||||||
const debug = D('@flecks/server/entry');
|
const debug = D('@flecks/server/entry');
|
||||||
debug('starting server...');
|
debug('starting server...');
|
||||||
try {
|
|
||||||
global.flecks = await Flecks.from({...runtime, flecks: await loadFlecks()});
|
global.flecks = await Flecks.from({...runtime, flecks: await loadFlecks()});
|
||||||
await global.flecks.invokeSequentialAsync('@flecks/server.up');
|
await global.flecks.invokeSequentialAsync('@flecks/server.up');
|
||||||
debug('up!');
|
debug('up!');
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
|
|
49
packages/server/src/server.js
Normal file
49
packages/server/src/server.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import cluster from 'cluster';
|
||||||
|
import {createConnection} from 'net';
|
||||||
|
|
||||||
|
const {
|
||||||
|
FLECKS_SERVER_TEST_SOCKET,
|
||||||
|
NODE_ENV,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/server.up': (flecks) => {
|
||||||
|
if (!FLECKS_SERVER_TEST_SOCKET || 'test' !== NODE_ENV) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const socket = createConnection(FLECKS_SERVER_TEST_SOCKET);
|
||||||
|
if (cluster.isWorker) {
|
||||||
|
cluster.worker.on('disconnect', () => {
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
flecks.server.socket = socket;
|
||||||
|
socket.on('connect', () => {
|
||||||
|
socket.on('data', (data) => {
|
||||||
|
const {meta, payload, type} = JSON.parse(data);
|
||||||
|
switch (type) {
|
||||||
|
case 'config.get':
|
||||||
|
socket.write(JSON.stringify({
|
||||||
|
meta,
|
||||||
|
payload: flecks.get(payload),
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
socket.end();
|
||||||
|
process.exit(payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mixin = (Flecks) => class FlecksWithServer extends Flecks {
|
||||||
|
|
||||||
|
constructor(runtime) {
|
||||||
|
super(runtime);
|
||||||
|
this.server = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -2,9 +2,14 @@ import {cp} from 'fs/promises';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {createWorkspace} from '@flecks/core/build/testing';
|
import {createWorkspace} from '@flecks/core/build/testing';
|
||||||
import {binaryPath, processCode, spawnWith} from '@flecks/core/server';
|
import {
|
||||||
|
binaryPath,
|
||||||
|
pipesink,
|
||||||
|
processCode,
|
||||||
|
spawnWith,
|
||||||
|
} from '@flecks/core/server';
|
||||||
|
|
||||||
import {listen} from './listen';
|
import {socketListener} from './listen';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
@ -18,43 +23,85 @@ export async function createApplication() {
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildChild(path, {args = [], opts = {}} = {}) {
|
class TestingServer {
|
||||||
return spawnWith(
|
|
||||||
|
constructor(path, child, socketServer) {
|
||||||
|
this.path = path;
|
||||||
|
this.child = child;
|
||||||
|
this.socketServer = socketServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForSocket(options) {
|
||||||
|
return this.socketServer.waitForSocket(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startServer({
|
||||||
|
args = ['-h'],
|
||||||
|
beforeBuild,
|
||||||
|
failOnErrorCode = true,
|
||||||
|
opts = {},
|
||||||
|
path: request,
|
||||||
|
task,
|
||||||
|
} = {}) {
|
||||||
|
let previousTimeout;
|
||||||
|
const start = Date.now();
|
||||||
|
if (task) {
|
||||||
|
previousTimeout = task.timeout();
|
||||||
|
task.timeout(0);
|
||||||
|
}
|
||||||
|
const {socketPath, socketServer} = await socketListener();
|
||||||
|
const path = request || await createApplication();
|
||||||
|
if (beforeBuild) {
|
||||||
|
await beforeBuild({path, task});
|
||||||
|
}
|
||||||
|
const server = spawnWith(
|
||||||
[await binaryPath('flecks', '@flecks/build'), 'build', ...args],
|
[await binaryPath('flecks', '@flecks/build'), 'build', ...args],
|
||||||
{
|
{
|
||||||
stdio: 'ignore',
|
stdio: 'pipe',
|
||||||
...opts,
|
...opts,
|
||||||
env: {
|
env: {
|
||||||
FLECKS_ENV__flecks_server__stats: '{"preset": "none"}',
|
FLECKS_ENV__flecks_server__stats: '{"preset": "none"}',
|
||||||
FLECKS_ENV__flecks_server__start: 0,
|
FLECKS_ENV__flecks_server__start: true,
|
||||||
FLECKS_CORE_ROOT: path,
|
FLECKS_CORE_ROOT: path,
|
||||||
|
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
||||||
|
NODE_ENV: 'test',
|
||||||
NODE_PATH: join(FLECKS_CORE_ROOT, '..', '..', 'node_modules'),
|
NODE_PATH: join(FLECKS_CORE_ROOT, '..', '..', 'node_modules'),
|
||||||
...opts.env,
|
...opts.env,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
if (failOnErrorCode) {
|
||||||
|
const stderr = pipesink(server.stderr);
|
||||||
export async function build(path, {args = [], opts = {}} = {}) {
|
server.on('exit', async (code) => {
|
||||||
return processCode(await buildChild(path, {args, opts}));
|
if (0 !== code) {
|
||||||
}
|
const buffer = await stderr;
|
||||||
|
if (!process.stderr.write(buffer)) {
|
||||||
export async function serverActions(path, actions) {
|
await new Promise((resolve, reject) => {
|
||||||
const {listening, path: socketPath, socketServer} = await listen();
|
process.stderr.on('error', reject);
|
||||||
await listening;
|
process.stderr.on('drain', resolve);
|
||||||
const server = spawnWith(
|
});
|
||||||
['node', join(path, 'dist', 'server')],
|
}
|
||||||
{
|
// eslint-disable-next-line no-console
|
||||||
env: {
|
console.error('\nserver process exited unexpectedly\n');
|
||||||
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
process.exit(code);
|
||||||
NODE_PATH: join(FLECKS_CORE_ROOT, '..', '..', 'node_modules'),
|
}
|
||||||
},
|
});
|
||||||
stdio: 'ignore',
|
}
|
||||||
},
|
task?.timeout(previousTimeout + (Date.now() - start));
|
||||||
|
return new TestingServer(
|
||||||
|
path,
|
||||||
|
server,
|
||||||
|
socketServer,
|
||||||
);
|
);
|
||||||
const [code, results] = await Promise.all([
|
}
|
||||||
processCode(server),
|
|
||||||
socketServer.waitForSocket().then(async (socket) => {
|
export function withServer(task, options) {
|
||||||
|
return async function withServer() {
|
||||||
|
const server = await startServer({...options, task: this});
|
||||||
|
const socket = await server.waitForSocket({task: this});
|
||||||
|
server.actions = async (actions) => {
|
||||||
const results = [];
|
const results = [];
|
||||||
await actions.reduce(
|
await actions.reduce(
|
||||||
(p, action) => (
|
(p, action) => (
|
||||||
|
@ -68,7 +115,25 @@ export async function serverActions(path, actions) {
|
||||||
Promise.resolve(),
|
Promise.resolve(),
|
||||||
);
|
);
|
||||||
return results;
|
return results;
|
||||||
}),
|
};
|
||||||
]);
|
return task({server, socket});
|
||||||
return {code, results};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function build(path, {args = [], opts = {}} = {}) {
|
||||||
|
return processCode(spawnWith(
|
||||||
|
[await binaryPath('flecks', '@flecks/build'), 'build', ...args],
|
||||||
|
{
|
||||||
|
stdio: 'ignore',
|
||||||
|
...opts,
|
||||||
|
env: {
|
||||||
|
FLECKS_ENV__flecks_server__stats: '{"preset": "none"}',
|
||||||
|
FLECKS_ENV__flecks_server__start: 0,
|
||||||
|
FLECKS_CORE_ROOT: path,
|
||||||
|
NODE_ENV: 'test',
|
||||||
|
NODE_PATH: join(FLECKS_CORE_ROOT, '..', '..', 'node_modules'),
|
||||||
|
...opts.env,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,12 @@ class SocketWrapper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForHmr() {
|
async waitForAction(type) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.socket.on('error', reject);
|
this.socket.on('error', reject);
|
||||||
this.socket.on('data', (data) => {
|
this.socket.on('data', (data) => {
|
||||||
const action = JSON.parse(data.toString());
|
const action = JSON.parse(data.toString());
|
||||||
if ('hmr' === action.type) {
|
if (action.type === type) {
|
||||||
resolve(action);
|
resolve(action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -47,31 +47,39 @@ class SocketWrapper {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listen() {
|
export async function socketListener() {
|
||||||
const path = join(tmpdir(), 'flecks', 'ci', await id());
|
const path = join(tmpdir(), 'flecks', 'ci', await id());
|
||||||
await mkdir(dirname(path), {recursive: true});
|
await mkdir(dirname(path), {recursive: true});
|
||||||
const server = createServer();
|
const server = createServer();
|
||||||
server.listen(path);
|
server.listen(path);
|
||||||
server.waitForSocket = () => (
|
server.waitForSocket = ({task, timeout = 30000} = {}) => (
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
server.on('error', reject);
|
let previousTimeout;
|
||||||
|
const start = Date.now();
|
||||||
|
if (task) {
|
||||||
|
previousTimeout = task.timeout();
|
||||||
|
task.timeout(0);
|
||||||
|
}
|
||||||
|
const handle = setTimeout(() => {
|
||||||
|
reject(new Error('timeout waiting for IPC connection'));
|
||||||
|
}, timeout);
|
||||||
|
const finish = () => {
|
||||||
|
clearTimeout(handle);
|
||||||
|
task?.timeout(previousTimeout + (Date.now() - start));
|
||||||
|
};
|
||||||
|
server.on('error', (error) => {
|
||||||
|
finish();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
server.on('connection', (socket) => {
|
server.on('connection', (socket) => {
|
||||||
|
finish();
|
||||||
resolve(new SocketWrapper(socket));
|
resolve(new SocketWrapper(socket));
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return {
|
await new Promise((resolve, reject) => {
|
||||||
listening: new Promise((resolve, reject) => {
|
|
||||||
server.on('error', reject);
|
server.on('error', reject);
|
||||||
server.on('listening', resolve);
|
server.on('listening', resolve);
|
||||||
}),
|
});
|
||||||
path,
|
return {socketServer: server, socketPath: path};
|
||||||
socketServer: server,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function socketListener() {
|
|
||||||
const {listening, path: socketPath, socketServer} = await listen();
|
|
||||||
await listening;
|
|
||||||
return {socketServer, socketPath};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,15 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication} from './build/build';
|
import {withServer} from './build/build';
|
||||||
import {socketListener} from './build/listen';
|
|
||||||
|
|
||||||
let path;
|
it('allows updates to fail', withServer(async ({server, socket}) => {
|
||||||
let socket;
|
|
||||||
|
|
||||||
before(heavySetup(async () => {
|
|
||||||
path = await createApplication();
|
|
||||||
const {socketPath, socketServer} = await socketListener();
|
|
||||||
build(
|
|
||||||
path,
|
|
||||||
{
|
|
||||||
args: ['-h'],
|
|
||||||
opts: {
|
|
||||||
env: {
|
|
||||||
FLECKS_ENV__flecks_server__start: true,
|
|
||||||
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
socket = await socketServer.waitForSocket();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('updates config', async () => {
|
|
||||||
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
|
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
|
||||||
.to.equal('bar');
|
.to.equal('bar');
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(path, 'build', 'flecks.yml'),
|
join(server.path, 'build', 'flecks.yml'),
|
||||||
`
|
`
|
||||||
'@flecks/build': {}
|
'@flecks/build': {}
|
||||||
'@flecks/core': {}
|
'@flecks/core': {}
|
||||||
|
@ -40,12 +17,12 @@ it('updates config', async () => {
|
||||||
'comm:./comm': {foo: 'baz'}
|
'comm:./comm': {foo: 'baz'}
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
await socket.waitForHmr();
|
await socket.waitForAction('hmr');
|
||||||
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
|
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
|
||||||
.to.equal('baz');
|
.to.equal('baz');
|
||||||
let restarted;
|
let restarted;
|
||||||
const whatHappened = Promise.race([
|
const whatHappened = Promise.race([
|
||||||
socket.waitForHmr()
|
socket.waitForAction('hmr')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
restarted = false;
|
restarted = false;
|
||||||
})
|
})
|
||||||
|
@ -58,7 +35,7 @@ it('updates config', async () => {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(path, 'build', 'flecks.yml'),
|
join(server.path, 'build', 'flecks.yml'),
|
||||||
`
|
`
|
||||||
'@flecks/build': {}
|
'@flecks/build': {}
|
||||||
'@flecks/core': {}
|
'@flecks/core': {}
|
||||||
|
@ -69,4 +46,4 @@ it('updates config', async () => {
|
||||||
await whatHappened;
|
await whatHappened;
|
||||||
expect(restarted)
|
expect(restarted)
|
||||||
.to.be.true;
|
.to.be.true;
|
||||||
});
|
}));
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {buildChild, createApplication} from './build/build';
|
import {withServer} from './build/build';
|
||||||
import {socketListener} from './build/listen';
|
|
||||||
|
|
||||||
let path;
|
it('restarts when config keys change', withServer(async ({server, socket}) => {
|
||||||
let listener;
|
|
||||||
let socket;
|
|
||||||
|
|
||||||
before(heavySetup(async () => {
|
|
||||||
path = await createApplication();
|
|
||||||
listener = await socketListener();
|
|
||||||
const {socketPath, socketServer} = listener;
|
|
||||||
await buildChild(
|
|
||||||
path,
|
|
||||||
{
|
|
||||||
args: ['-w'],
|
|
||||||
opts: {
|
|
||||||
env: {
|
|
||||||
FLECKS_ENV__flecks_server__start: true,
|
|
||||||
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
socket = await socketServer.waitForSocket();
|
|
||||||
}));
|
|
||||||
|
|
||||||
async function restart() {
|
|
||||||
this.timeout(0);
|
|
||||||
let restarted;
|
let restarted;
|
||||||
const whatHappened = Promise.race([
|
const whatHappened = Promise.race([
|
||||||
socket.waitForHmr()
|
socket.waitForAction('hmr')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
restarted = false;
|
restarted = false;
|
||||||
})
|
})
|
||||||
|
@ -47,7 +21,7 @@ async function restart() {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(path, 'build', 'flecks.yml'),
|
join(server.path, 'build', 'flecks.yml'),
|
||||||
`
|
`
|
||||||
'@flecks/build': {}
|
'@flecks/build': {}
|
||||||
'@flecks/core': {}
|
'@flecks/core': {}
|
||||||
|
@ -60,15 +34,10 @@ async function restart() {
|
||||||
expect(restarted)
|
expect(restarted)
|
||||||
.to.be.true;
|
.to.be.true;
|
||||||
let config;
|
let config;
|
||||||
const before = Date.now();
|
await server.socketServer.waitForSocket({task: this})
|
||||||
await listener.socketServer.waitForSocket()
|
|
||||||
.then(async (socket) => {
|
.then(async (socket) => {
|
||||||
({payload: config} = await socket.send({type: 'config.get', payload: '@flecks/repl/server'}));
|
({payload: config} = await socket.send({type: 'config.get', payload: '@flecks/repl/server'}));
|
||||||
});
|
});
|
||||||
// Had to rebuild...
|
|
||||||
this.timeout(2000 + (Date.now() - before));
|
|
||||||
expect(config)
|
expect(config)
|
||||||
.to.not.be.undefined;
|
.to.not.be.undefined;
|
||||||
}
|
}));
|
||||||
|
|
||||||
it('restarts when config keys change', restart);
|
|
||||||
|
|
|
@ -1,38 +1,15 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication} from './build/build';
|
import {withServer} from './build/build';
|
||||||
import {socketListener} from './build/listen';
|
|
||||||
|
|
||||||
let path;
|
it('updates config', withServer(async ({server, socket}) => {
|
||||||
let socket;
|
|
||||||
|
|
||||||
before(heavySetup(async () => {
|
|
||||||
path = await createApplication();
|
|
||||||
const {socketPath, socketServer} = await socketListener();
|
|
||||||
build(
|
|
||||||
path,
|
|
||||||
{
|
|
||||||
args: ['-h'],
|
|
||||||
opts: {
|
|
||||||
env: {
|
|
||||||
FLECKS_ENV__flecks_server__start: true,
|
|
||||||
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
socket = await socketServer.waitForSocket();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('updates config', async () => {
|
|
||||||
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
|
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
|
||||||
.to.equal('flecks');
|
.to.equal('flecks');
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(path, 'build', 'flecks.yml'),
|
join(server.path, 'build', 'flecks.yml'),
|
||||||
`
|
`
|
||||||
'@flecks/build': {}
|
'@flecks/build': {}
|
||||||
'@flecks/core': {id: 'testing'}
|
'@flecks/core': {id: 'testing'}
|
||||||
|
@ -40,8 +17,8 @@ it('updates config', async () => {
|
||||||
'comm:./comm': {}
|
'comm:./comm': {}
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
await socket.waitForHmr();
|
await socket.waitForAction('hmr');
|
||||||
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
|
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
|
||||||
.to.equal('testing');
|
.to.equal('testing');
|
||||||
await socket.send({type: 'exit'});
|
await socket.send({type: 'exit'});
|
||||||
});
|
}));
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import {mkdir} from 'fs/promises';
|
import {mkdir} from 'fs/promises';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication, serverActions} from './build/build';
|
import {withServer} from './build/build';
|
||||||
|
|
||||||
let path;
|
it('propagates bootstrap config', withServer(
|
||||||
|
async ({server}) => {
|
||||||
before(heavySetup(async () => {
|
const [{payload: foo}, {payload: blah}] = await server.actions([
|
||||||
path = await createApplication();
|
{type: 'config.get', payload: 'server-only.foo'},
|
||||||
|
{type: 'config.get', payload: 'server-only.blah'},
|
||||||
|
{type: 'exit'},
|
||||||
|
]);
|
||||||
|
expect(foo)
|
||||||
|
.to.equal('baz');
|
||||||
|
expect(blah)
|
||||||
|
.to.deep.equal({one: 2, three: 4});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
beforeBuild: async ({path}) => {
|
||||||
await mkdir(join(path, 'server-only', 'build'), {recursive: true});
|
await mkdir(join(path, 'server-only', 'build'), {recursive: true});
|
||||||
await writeFile(join(path, 'server-only', 'package.json'), '{}');
|
await writeFile(join(path, 'server-only', 'package.json'), '{}');
|
||||||
const config = `
|
const config = `
|
||||||
|
@ -32,17 +41,6 @@ before(heavySetup(async () => {
|
||||||
'server-only:./server-only': {foo: 'baz'}
|
'server-only:./server-only': {foo: 'baz'}
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
await build(path, {args: ['-d']});
|
},
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
it('propagates bootstrap config', async () => {
|
|
||||||
const {results: [{payload: foo}, {payload: blah}]} = await serverActions(path, [
|
|
||||||
{type: 'config.get', payload: 'server-only.foo'},
|
|
||||||
{type: 'config.get', payload: 'server-only.blah'},
|
|
||||||
{type: 'exit'},
|
|
||||||
]);
|
|
||||||
expect(foo)
|
|
||||||
.to.equal('baz');
|
|
||||||
expect(blah)
|
|
||||||
.to.deep.equal({one: 2, three: 4});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication, serverActions} from './build/build';
|
import {withServer} from './build/build';
|
||||||
|
|
||||||
let path;
|
it('propagates bootstrap config', withServer(
|
||||||
|
async ({server}) => {
|
||||||
before(heavySetup(async () => {
|
const [{payload: id}, {payload: foo}] = await server.actions([
|
||||||
path = await createApplication();
|
{type: 'config.get', payload: '@flecks/core.id'},
|
||||||
|
{type: 'config.get', payload: 'comm.foo'},
|
||||||
|
{type: 'exit'},
|
||||||
|
]);
|
||||||
|
expect(id)
|
||||||
|
.to.equal('testing');
|
||||||
|
expect(foo)
|
||||||
|
.to.equal('baz');
|
||||||
|
},
|
||||||
|
{
|
||||||
|
beforeBuild: async ({path}) => {
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(path, 'build', 'flecks.yml'),
|
join(path, 'build', 'flecks.yml'),
|
||||||
`
|
`
|
||||||
|
@ -19,17 +28,6 @@ before(heavySetup(async () => {
|
||||||
'comm:./comm': {foo: 'baz'}
|
'comm:./comm': {foo: 'baz'}
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
await build(path, {args: ['-d']});
|
},
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
it('propagates bootstrap config', async () => {
|
|
||||||
const {results: [{payload: id}, {payload: foo}]} = await serverActions(path, [
|
|
||||||
{type: 'config.get', payload: '@flecks/core.id'},
|
|
||||||
{type: 'config.get', payload: 'comm.foo'},
|
|
||||||
{type: 'exit'},
|
|
||||||
]);
|
|
||||||
expect(id)
|
|
||||||
.to.equal('testing');
|
|
||||||
expect(foo)
|
|
||||||
.to.equal('baz');
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication, serverActions} from './build/build';
|
import {withServer} from './build/build';
|
||||||
|
|
||||||
let path;
|
it('propagates runtime config', withServer(async ({server}) => {
|
||||||
|
const [{payload: foo}] = await server.actions([
|
||||||
before(heavySetup(async () => {
|
|
||||||
path = await createApplication();
|
|
||||||
await build(path, {args: ['-d']});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('propagates runtime config', async () => {
|
|
||||||
const {results: [{payload: foo}]} = await serverActions(path, [
|
|
||||||
{type: 'config.get', payload: 'comm.foo'},
|
{type: 'config.get', payload: 'comm.foo'},
|
||||||
{type: 'exit'},
|
{type: 'exit'},
|
||||||
]);
|
]);
|
||||||
expect(foo)
|
expect(foo)
|
||||||
.to.equal('bar');
|
.to.equal('bar');
|
||||||
});
|
}));
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
import {processCode} from '@flecks/core/src/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {build, createApplication, serverActions} from './build/build';
|
import {withServer} from './build/build';
|
||||||
|
|
||||||
let path;
|
it('connects', withServer(
|
||||||
|
async ({server}) => {
|
||||||
before(heavySetup(async () => {
|
const code = processCode(server.child);
|
||||||
path = await createApplication();
|
await server.actions([
|
||||||
await build(path, {args: ['-d']});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('connects', async () => {
|
|
||||||
const {code} = await serverActions(path, [
|
|
||||||
{type: 'exit', payload: 42},
|
{type: 'exit', payload: 42},
|
||||||
]);
|
]);
|
||||||
expect(code)
|
expect(await code)
|
||||||
.to.equal(42);
|
.to.equal(42);
|
||||||
});
|
},
|
||||||
|
{failOnErrorCode: false},
|
||||||
|
));
|
||||||
|
|
|
@ -1,37 +1,14 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {heavySetup} from '@flecks/core/build/testing';
|
|
||||||
import {writeFile} from '@flecks/core/server';
|
import {writeFile} from '@flecks/core/server';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {buildChild, createApplication} from './build/build';
|
import {withServer} from './build/build';
|
||||||
import {socketListener} from './build/listen';
|
|
||||||
|
|
||||||
let path;
|
it('restarts when root sources change', withServer(async ({server, socket}) => {
|
||||||
let socket;
|
|
||||||
|
|
||||||
before(heavySetup(async () => {
|
|
||||||
path = await createApplication();
|
|
||||||
const {socketPath, socketServer} = await socketListener();
|
|
||||||
await buildChild(
|
|
||||||
path,
|
|
||||||
{
|
|
||||||
args: ['-w'],
|
|
||||||
opts: {
|
|
||||||
env: {
|
|
||||||
FLECKS_ENV__flecks_server__start: true,
|
|
||||||
FLECKS_SERVER_TEST_SOCKET: socketPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
socket = await socketServer.waitForSocket();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('restarts when root sources change', async () => {
|
|
||||||
let restarted;
|
let restarted;
|
||||||
const whatHappened = Promise.race([
|
const whatHappened = Promise.race([
|
||||||
socket.waitForHmr()
|
socket.waitForAction('hmr')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
restarted = false;
|
restarted = false;
|
||||||
})
|
})
|
||||||
|
@ -43,8 +20,8 @@ it('restarts when root sources change', async () => {
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
await writeFile(join(path, 'comm', 'package.json'), '{}');
|
await writeFile(join(server.path, 'comm', 'package.json'), '{}');
|
||||||
await whatHappened;
|
await whatHappened;
|
||||||
expect(restarted)
|
expect(restarted)
|
||||||
.to.be.true;
|
.to.be.true;
|
||||||
});
|
}));
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
import cluster from 'cluster';
|
|
||||||
import {createConnection} from 'net';
|
|
||||||
|
|
||||||
const {
|
|
||||||
FLECKS_SERVER_TEST_SOCKET,
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/core.reload': (fleck, config) => {
|
'@flecks/core.reload': (fleck, config) => {
|
||||||
if ('comm' === fleck && 'fail' === config.foo) {
|
if ('comm' === fleck && 'fail' === config.foo) {
|
||||||
|
@ -12,42 +5,10 @@ export const hooks = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'@flecks/core.hmr': async (path, M, flecks) => {
|
'@flecks/core.hmr': async (path, M, flecks) => {
|
||||||
if (!flecks.socket) {
|
const {socket} = flecks.server;
|
||||||
return;
|
socket.write(JSON.stringify({
|
||||||
}
|
|
||||||
flecks.socket.write(JSON.stringify({
|
|
||||||
type: 'hmr',
|
type: 'hmr',
|
||||||
payload: path,
|
payload: path,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
'@flecks/server.up': async (flecks) => {
|
|
||||||
if (!FLECKS_SERVER_TEST_SOCKET) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const socket = createConnection(FLECKS_SERVER_TEST_SOCKET);
|
|
||||||
if (cluster.isWorker) {
|
|
||||||
cluster.worker.on('disconnect', () => {
|
|
||||||
socket.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
flecks.socket = socket;
|
|
||||||
socket.on('connect', () => {
|
|
||||||
socket.on('data', (data) => {
|
|
||||||
const {meta, payload, type} = JSON.parse(data);
|
|
||||||
switch (type) {
|
|
||||||
case 'config.get':
|
|
||||||
socket.write(JSON.stringify({
|
|
||||||
meta,
|
|
||||||
payload: flecks.get(payload),
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
case 'exit':
|
|
||||||
socket.end();
|
|
||||||
process.exit(payload);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
const {stat, unlink} = require('fs/promises');
|
const {stat, unlink} = require('fs/promises');
|
||||||
const {join} = require('path');
|
const {
|
||||||
|
basename,
|
||||||
|
dirname,
|
||||||
|
extname,
|
||||||
|
join,
|
||||||
|
relative,
|
||||||
|
} = require('path');
|
||||||
|
|
||||||
const Build = require('@flecks/build/build/build');
|
const Build = require('@flecks/build/build/build');
|
||||||
const {regexFromExtensions} = require('@flecks/build/src/server');
|
const {regexFromExtensions} = require('@flecks/build/src/server');
|
||||||
const {binaryPath, spawnWith} = require('@flecks/core/src/server');
|
const {binaryPath, glob, spawnWith} = require('@flecks/core/src/server');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
|
const tests = join(FLECKS_CORE_ROOT, 'test');
|
||||||
|
|
||||||
exports.hooks = {
|
exports.hooks = {
|
||||||
'@flecks/build.config': async (target, config, env, argv, flecks) => {
|
'@flecks/build.config': async (target, config, env, argv, flecks) => {
|
||||||
const isProduction = 'production' === argv.mode;
|
const isProduction = 'production' === argv.mode;
|
||||||
|
@ -25,6 +33,19 @@ exports.hooks = {
|
||||||
config.plugins.push(new MiniCssExtractPlugin({filename: 'assets/[name].css'}));
|
config.plugins.push(new MiniCssExtractPlugin({filename: 'assets/[name].css'}));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'test': {
|
||||||
|
finalLoader = {loader: MiniCssExtractPlugin.loader};
|
||||||
|
config.plugins.push(new MiniCssExtractPlugin({filename: 'assets/[name].css'}));
|
||||||
|
(await glob(join(tests, 'client', '*.js')))
|
||||||
|
.forEach((path) => {
|
||||||
|
const entry = relative(tests, path);
|
||||||
|
config.entry[join(dirname(entry), basename(entry, extname(entry)))] = [
|
||||||
|
'source-map-support/register',
|
||||||
|
path,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'web': {
|
case 'web': {
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
finalLoader = {loader: MiniCssExtractPlugin.loader};
|
finalLoader = {loader: MiniCssExtractPlugin.loader};
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
const {access, readFile} = require('fs/promises');
|
const {access} = require('fs/promises');
|
||||||
const {
|
const {
|
||||||
basename,
|
basename,
|
||||||
dirname,
|
|
||||||
extname,
|
extname,
|
||||||
join,
|
join,
|
||||||
} = require('path');
|
} = require('path');
|
||||||
|
|
||||||
const Build = require('@flecks/build/build/build');
|
const Build = require('@flecks/build/build/build');
|
||||||
const {glob} = require('@flecks/core/server');
|
|
||||||
|
|
||||||
module.exports = async (config, env, argv, flecks) => {
|
module.exports = async (config, env, argv, flecks) => {
|
||||||
const buildFlecks = await Build.from({
|
const buildFlecks = await Build.from({
|
||||||
config: flecks.realiasedConfig,
|
config: flecks.realiasedConfig,
|
||||||
platforms: ['client', '!server'],
|
platforms: ['client', '!server'],
|
||||||
});
|
});
|
||||||
const {resolver, flecks: webFlecks} = buildFlecks;
|
const {flecks: webFlecks} = buildFlecks;
|
||||||
const paths = Object.keys(webFlecks)
|
const paths = Object.keys(webFlecks)
|
||||||
.filter((fleck) => !['@flecks/server'].includes(fleck));
|
.filter((fleck) => !['@flecks/server'].includes(fleck));
|
||||||
const styles = (
|
const styles = (
|
||||||
|
@ -44,7 +42,6 @@ module.exports = async (config, env, argv, flecks) => {
|
||||||
)
|
)
|
||||||
.filter((filename) => !!filename);
|
.filter((filename) => !!filename);
|
||||||
const runtime = await flecks.resolver.resolve(join('@flecks/web/runtime'));
|
const runtime = await flecks.resolver.resolve(join('@flecks/web/runtime'));
|
||||||
const isProduction = 'production' === argv.mode;
|
|
||||||
const resolvedPaths = (await Promise.all(
|
const resolvedPaths = (await Promise.all(
|
||||||
paths.map(async (path) => [path, await flecks.resolver.resolve(path)]),
|
paths.map(async (path) => [path, await flecks.resolver.resolve(path)]),
|
||||||
))
|
))
|
||||||
|
@ -98,56 +95,4 @@ module.exports = async (config, env, argv, flecks) => {
|
||||||
await buildFlecks.runtimeCompiler('web', config, env, argv);
|
await buildFlecks.runtimeCompiler('web', config, env, argv);
|
||||||
// Styles.
|
// Styles.
|
||||||
config.entry.index.push(...styles);
|
config.entry.index.push(...styles);
|
||||||
// Tests.
|
|
||||||
if (!isProduction) {
|
|
||||||
const testEntries = (await Promise.all(
|
|
||||||
buildFlecks.roots
|
|
||||||
.map(async ([root, request]) => {
|
|
||||||
const tests = [];
|
|
||||||
const resolved = dirname(
|
|
||||||
await buildFlecks.resolver.resolve(join(request, 'package.json')),
|
|
||||||
);
|
|
||||||
const rootTests = await glob(join(resolved, 'test', '*.js'));
|
|
||||||
tests.push(...rootTests.map((test) => test.replace(resolved, root)));
|
|
||||||
const platformTests = await Promise.all(
|
|
||||||
buildFlecks.platforms.map((platform) => (
|
|
||||||
glob(join(resolved, 'test', platform, '*.js'))
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
tests.push(...platformTests.flat().map((test) => test.replace(resolved, root)));
|
|
||||||
return [root, tests];
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
.filter(([, tests]) => tests.length > 0);
|
|
||||||
const tests = await resolver.resolve(
|
|
||||||
join('@flecks/web', 'server', 'build', 'tests'),
|
|
||||||
);
|
|
||||||
const testsSource = (await readFile(tests)).toString();
|
|
||||||
config.module.rules.push({
|
|
||||||
test: tests,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: runtime,
|
|
||||||
options: {
|
|
||||||
source: testsSource.replace(
|
|
||||||
" await import('@flecks/web/tests');",
|
|
||||||
testEntries
|
|
||||||
.map(([root, tests]) => (
|
|
||||||
[
|
|
||||||
` describe('${root}', () => {`,
|
|
||||||
` ${tests.map((test) => `require('${test}');`).join('\n ')}`,
|
|
||||||
' });',
|
|
||||||
].join('\n')
|
|
||||||
)).join('\n\n'),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
// Fix a little derp in mocha 10.2.0.
|
|
||||||
config.module.rules.push({
|
|
||||||
test: /mocha\/mocha\.js$/,
|
|
||||||
use: await flecks.resolver.resolve('@flecks/web/build/fix-mocha-critical-dependency'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ const WaitForManifestPlugin = require('./wait-for-manifest');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
NODE_ENV,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
module.exports = async (env, argv, flecks) => {
|
module.exports = async (env, argv, flecks) => {
|
||||||
|
@ -70,12 +71,6 @@ module.exports = async (env, argv, flecks) => {
|
||||||
entry: '@flecks/web/server/build/entry',
|
entry: '@flecks/web/server/build/entry',
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
if (!isProduction) {
|
|
||||||
entries.push(['tests', {
|
|
||||||
entry: '@flecks/web/server/build/tests',
|
|
||||||
title: 'Testbed',
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
entries
|
entries
|
||||||
.map(async ([name, mainsConfig]) => {
|
.map(async ([name, mainsConfig]) => {
|
||||||
|
@ -195,6 +190,7 @@ module.exports = async (env, argv, flecks) => {
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: isProduction,
|
minimize: isProduction,
|
||||||
|
nodeEnv: NODE_ENV,
|
||||||
runtimeChunk: 'single',
|
runtimeChunk: 'single',
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
cacheGroups: {
|
cacheGroups: {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
"client.js",
|
||||||
"entry.js",
|
"entry.js",
|
||||||
"index.js",
|
"index.js",
|
||||||
"runtime.js",
|
"runtime.js",
|
||||||
|
@ -29,11 +30,13 @@
|
||||||
"@babel/parser": "^7.17.0",
|
"@babel/parser": "^7.17.0",
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
"@flecks/core": "^4.0.5",
|
"@flecks/core": "^4.0.5",
|
||||||
|
"@flecks/server": "^4.0.5",
|
||||||
"@webpack-cli/serve": "^2.0.5",
|
"@webpack-cli/serve": "^2.0.5",
|
||||||
"add-asset-html-webpack-plugin": "^6.0.0",
|
"add-asset-html-webpack-plugin": "^6.0.0",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"before-build-webpack": "^0.2.13",
|
"before-build-webpack": "^0.2.13",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
"browserify-zlib": "^0.2.0",
|
"browserify-zlib": "^0.2.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"clean-webpack-plugin": "4.0.0",
|
"clean-webpack-plugin": "4.0.0",
|
||||||
|
@ -42,8 +45,8 @@
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"html-loader": "^4.2.0",
|
"html-loader": "^4.2.0",
|
||||||
"html-webpack-plugin": "^5.5.3",
|
"html-webpack-plugin": "^5.5.3",
|
||||||
"husky": "^9.0.7",
|
|
||||||
"http-proxy": "^1.17.0",
|
"http-proxy": "^1.17.0",
|
||||||
|
"husky": "^9.0.7",
|
||||||
"mini-css-extract-plugin": "^2.7.6",
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"null-loader": "^4.0.1",
|
"null-loader": "^4.0.1",
|
||||||
|
@ -61,6 +64,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@flecks/build": "^4.0.5",
|
"@flecks/build": "^4.0.5",
|
||||||
"@flecks/fleck": "^4.0.5"
|
"@flecks/fleck": "^4.0.5",
|
||||||
|
"puppeteer": "^22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
packages/web/src/client.js
Normal file
24
packages/web/src/client.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export const mixin = (Flecks) => class FlecksWithWebClient extends Flecks {
|
||||||
|
|
||||||
|
constructor(runtime) {
|
||||||
|
super(runtime);
|
||||||
|
if ('test' !== process.env.NODE_ENV) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.web = {
|
||||||
|
test: ({payload, type}) => (
|
||||||
|
fetch(
|
||||||
|
`/@flecks/web/testing?type=${type}`,
|
||||||
|
{
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -3,6 +3,7 @@ import {createServer, ServerResponse} from 'http';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {D} from '@flecks/core';
|
import {D} from '@flecks/core';
|
||||||
|
import bodyParser from 'body-parser';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import httpProxy from 'http-proxy';
|
import httpProxy from 'http-proxy';
|
||||||
|
@ -31,6 +32,8 @@ export const createHttpServer = async (flecks) => {
|
||||||
const httpServer = createServer(app);
|
const httpServer = createServer(app);
|
||||||
httpServer.app = app;
|
httpServer.app = app;
|
||||||
flecks.web.server = httpServer;
|
flecks.web.server = httpServer;
|
||||||
|
// Body parser.
|
||||||
|
app.use(bodyParser.json());
|
||||||
// Compression. heheh
|
// Compression. heheh
|
||||||
app.use(compression({level: 'production' === NODE_ENV ? 6 : 9}));
|
app.use(compression({level: 'production' === NODE_ENV ? 6 : 9}));
|
||||||
// Socket connection.
|
// Socket connection.
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import {configSource, inlineConfig} from './config';
|
import {configSource, inlineConfig} from './config';
|
||||||
import {createHttpServer} from './http';
|
import {createHttpServer} from './http';
|
||||||
|
|
||||||
|
const {
|
||||||
|
NODE_ENV,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
export {configSource};
|
export {configSource};
|
||||||
|
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/web.routes': (flecks) => [
|
'@flecks/web.routes': (flecks) => {
|
||||||
|
const routes = [
|
||||||
{
|
{
|
||||||
method: 'get',
|
method: 'get',
|
||||||
path: '/flecks.config.js',
|
path: '/flecks.config.js',
|
||||||
|
@ -13,7 +18,19 @@ export const hooks = {
|
||||||
res.send(await configSource(flecks, req));
|
res.send(await configSource(flecks, req));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
if ('test' === NODE_ENV) {
|
||||||
|
routes.push({
|
||||||
|
method: 'post',
|
||||||
|
path: '/@flecks/web/testing',
|
||||||
|
middleware: (req, res, next) => {
|
||||||
|
flecks.server.socket.write(JSON.stringify({payload: req.body, type: req.query.type}));
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
},
|
||||||
'@flecks/web/server.stream.html': inlineConfig,
|
'@flecks/web/server.stream.html': inlineConfig,
|
||||||
'@flecks/server.up': (flecks) => createHttpServer(flecks),
|
'@flecks/server.up': (flecks) => createHttpServer(flecks),
|
||||||
};
|
};
|
||||||
|
|
41
packages/web/test/client/up.js
Normal file
41
packages/web/test/client/up.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import {expect} from 'chai';
|
||||||
|
|
||||||
|
import {withWeb} from '../helpers/with-web';
|
||||||
|
|
||||||
|
let report;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
beforeConnect: ({socket}) => {
|
||||||
|
report = socket.waitForAction('report');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('brings a client up', withWeb(
|
||||||
|
async function test({
|
||||||
|
browser,
|
||||||
|
page,
|
||||||
|
response,
|
||||||
|
}) {
|
||||||
|
expect(response)
|
||||||
|
.to.not.be.null;
|
||||||
|
const {
|
||||||
|
payload: {
|
||||||
|
config,
|
||||||
|
id,
|
||||||
|
request,
|
||||||
|
},
|
||||||
|
} = await report;
|
||||||
|
const appMountSelector = await page.waitForSelector(`#${id}`);
|
||||||
|
expect(await appMountSelector?.evaluate((el) => el.textContent))
|
||||||
|
.to.equal('hello world');
|
||||||
|
const yepSelector = await page.waitForSelector(`.${request}`);
|
||||||
|
expect(await yepSelector?.evaluate((el) => el.textContent))
|
||||||
|
.to.equal('YEP');
|
||||||
|
expect(config)
|
||||||
|
.to.deep.equal({why: 'hello there'});
|
||||||
|
expect(request)
|
||||||
|
.to.equal('testing-value-value');
|
||||||
|
await browser.close();
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
));
|
69
packages/web/test/helpers/with-web.js
Normal file
69
packages/web/test/helpers/with-web.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import {startServer} from '@flecks/server/test/server/build/build';
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
export async function connectBrowser(url, options = {}) {
|
||||||
|
let previousTimeout;
|
||||||
|
const start = Date.now();
|
||||||
|
if (options.task) {
|
||||||
|
previousTimeout = options.task.timeout();
|
||||||
|
options.task.timeout(0);
|
||||||
|
}
|
||||||
|
const {timeout = 30000} = options;
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
// For CI.
|
||||||
|
args: ['--no-sandbox'],
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
let response;
|
||||||
|
const handle = setTimeout(() => {
|
||||||
|
throw new Error(`timed out trying to connect browser to '${url}'!`);
|
||||||
|
}, timeout);
|
||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
while (!response) {
|
||||||
|
try {
|
||||||
|
response = await page.goto(url, {...options, timeout: timeout - (Date.now() - start)});
|
||||||
|
if (response) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 250);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* eslint-enable no-await-in-loop */
|
||||||
|
clearTimeout(handle);
|
||||||
|
options.task?.timeout(previousTimeout + (Date.now() - start));
|
||||||
|
return {browser, page, response};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withWeb(task, options) {
|
||||||
|
return async function withWeb() {
|
||||||
|
const server = await startServer({...options, task: this});
|
||||||
|
const socket = await server.waitForSocket({...options, task: this});
|
||||||
|
if (options.beforeConnect) {
|
||||||
|
await options.beforeConnect({server, socket});
|
||||||
|
}
|
||||||
|
const start = Date.now();
|
||||||
|
const previousTimeout = this.timeout();
|
||||||
|
this.timeout(0);
|
||||||
|
const {payload: config} = await socket.send({type: 'config.get', payload: '@flecks/web'});
|
||||||
|
this.timeout(previousTimeout + (Date.now() - start));
|
||||||
|
const {browser, page, response} = await connectBrowser(
|
||||||
|
// @todo schema
|
||||||
|
`http://${config.public}`,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
task: this,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return task({
|
||||||
|
browser,
|
||||||
|
page,
|
||||||
|
response,
|
||||||
|
server,
|
||||||
|
socket,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
4
packages/web/test/server/template/build/flecks.yml
Normal file
4
packages/web/test/server/template/build/flecks.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
'@flecks/core': {}
|
||||||
|
'@flecks/server': {}
|
||||||
|
'@flecks/web': {}
|
||||||
|
'test:./test': {}
|
1
packages/web/test/server/template/package.json
Normal file
1
packages/web/test/server/template/package.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
packages/web/test/server/template/test/package.json
Normal file
1
packages/web/test/server/template/test/package.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
13
packages/web/test/server/template/test/src/client.js
Normal file
13
packages/web/test/server/template/test/src/client.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/web/client.up': async (container, flecks) => {
|
||||||
|
container.innerHTML = 'hello world';
|
||||||
|
await flecks.web.test({
|
||||||
|
type: 'report',
|
||||||
|
payload: {
|
||||||
|
id: flecks.get('@flecks/web.appMountId'),
|
||||||
|
config: flecks.get('test'),
|
||||||
|
env: process.env.NODE_ENV,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
5
packages/web/test/server/template/test/src/index.js
Normal file
5
packages/web/test/server/template/test/src/index.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/web.config': () => ({
|
||||||
|
why: 'hello there',
|
||||||
|
}),
|
||||||
|
};
|
18
packages/web/test/server/template/test/src/server.js
Normal file
18
packages/web/test/server/template/test/src/server.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {Readable} from 'stream';
|
||||||
|
|
||||||
|
import {pipesink} from '@flecks/core/server';
|
||||||
|
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/web/server.request.route': () => (req, res, next) => {
|
||||||
|
req.body.request += '-value';
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
'@flecks/web/server.request.socket': () => (req, res, next) => {
|
||||||
|
req.body.request = 'testing-value';
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
'@flecks/web/server.stream.html': async (stream, req) => {
|
||||||
|
const html = (await pipesink(stream)).toString();
|
||||||
|
return Readable.from(html.replace('<body>', `<body><p class="${req.body.request}">YEP</p>`));
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user