|
|
|
import * as path from 'path';
|
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as http from 'http';
|
|
|
|
import { rollup } from 'rollup';
|
|
|
|
import virtual from '@rollup/plugin-virtual';
|
|
|
|
import puppeteer from 'puppeteer';
|
|
|
|
|
|
|
|
import {
|
|
|
|
loadConfig,
|
|
|
|
loadSvelte,
|
|
|
|
mkdirp,
|
|
|
|
prettyPrintPuppeteerAssertionError,
|
|
|
|
retryAsync,
|
|
|
|
executeBrowserTest
|
|
|
|
} from '../helpers';
|
|
|
|
import { deepEqual } from 'assert';
|
|
|
|
|
|
|
|
const page = `
|
|
|
|
<body>
|
|
|
|
<main></main>
|
|
|
|
<script src='/bundle.js'></script>
|
|
|
|
</body>
|
|
|
|
`;
|
|
|
|
|
|
|
|
let svelte;
|
|
|
|
let server;
|
|
|
|
let code;
|
|
|
|
let browser;
|
|
|
|
|
|
|
|
const internal = path.resolve('internal/index.mjs');
|
|
|
|
const index = path.resolve('index.mjs');
|
|
|
|
|
|
|
|
function create_server() {
|
|
|
|
return new Promise((fulfil, reject) => {
|
|
|
|
const server = http.createServer((req, res) => {
|
|
|
|
if (req.url === '/') {
|
|
|
|
res.end(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req.url === '/bundle.js') {
|
|
|
|
res.end(code);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
server.on('error', reject);
|
|
|
|
|
|
|
|
server.listen('6789', () => {
|
|
|
|
fulfil(server);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function launchPuppeteer() {
|
|
|
|
return await retryAsync(() => puppeteer.launch());
|
|
|
|
}
|
|
|
|
|
|
|
|
const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8');
|
|
|
|
|
|
|
|
describe('runtime (puppeteer)', () => {
|
|
|
|
before(async () => {
|
|
|
|
svelte = loadSvelte(false);
|
|
|
|
console.log('[runtime-puppeteer] Loaded Svelte');
|
|
|
|
server = await create_server();
|
|
|
|
console.log('[runtime-puppeteer] Started server');
|
|
|
|
browser = await launchPuppeteer();
|
|
|
|
console.log('[runtime-puppeteer] Launched puppeteer browser');
|
|
|
|
});
|
|
|
|
|
|
|
|
after(async () => {
|
|
|
|
if (server) server.close();
|
|
|
|
if (browser) await browser.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
const failed = new Set();
|
|
|
|
|
|
|
|
function runTest(dir, hydrate, is_first_run) {
|
|
|
|
if (dir[0] === '.') return;
|
|
|
|
// MEMO: puppeteer can not execute Chromium properly with Node8,10 on Linux at GitHub actions.
|
|
|
|
const { version } = process;
|
|
|
|
if ((version.startsWith('v8.') || version.startsWith('v10.')) && process.platform === 'linux') return;
|
|
|
|
|
|
|
|
const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
|
|
|
|
const solo = config.solo || /\.solo/.test(dir);
|
|
|
|
const skip = config.skip || /\.skip/.test(dir);
|
|
|
|
|
|
|
|
if (hydrate && config.skip_if_hydrate) return;
|
|
|
|
|
|
|
|
if (solo && process.env.CI) {
|
|
|
|
throw new Error('Forgot to remove `solo: true` from test');
|
|
|
|
}
|
|
|
|
|
|
|
|
(skip ? it.skip : solo ? it.only : it)(`${dir} ${hydrate ? '(with hydration)' : ''}`, async () => {
|
|
|
|
if (failed.has(dir)) {
|
|
|
|
// this makes debugging easier, by only printing compiled output once
|
|
|
|
throw new Error('skipping test, already failed');
|
|
|
|
}
|
|
|
|
|
|
|
|
const warnings = [];
|
|
|
|
|
|
|
|
const bundle = await rollup({
|
|
|
|
input: 'main',
|
|
|
|
plugins: [
|
|
|
|
{
|
|
|
|
name: 'testing-runtime-puppeteer',
|
|
|
|
resolveId(importee) {
|
|
|
|
if (importee === 'svelte/internal' || importee === './internal') {
|
|
|
|
return internal;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (importee === 'svelte') {
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (importee === 'main') {
|
|
|
|
return 'main';
|
|
|
|
}
|
|
|
|
},
|
|
|
|
load(id) {
|
|
|
|
if (id === 'main') {
|
|
|
|
return `
|
|
|
|
import SvelteComponent from ${JSON.stringify(
|
|
|
|
path.join(__dirname, 'samples', dir, 'main.svelte')
|
|
|
|
)};
|
|
|
|
import config from ${JSON.stringify(
|
|
|
|
path.join(__dirname, 'samples', dir, '_config.js')
|
|
|
|
)};
|
|
|
|
import * as assert from 'assert';
|
|
|
|
|
|
|
|
export default async function (target) {
|
|
|
|
let unhandled_rejection = false;
|
|
|
|
function unhandled_rejection_handler(event) {
|
|
|
|
unhandled_rejection = event.reason;
|
|
|
|
}
|
|
|
|
window.addEventListener('unhandledrejection', unhandled_rejection_handler);
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (config.before_test) config.before_test();
|
|
|
|
|
|
|
|
const options = Object.assign({}, {
|
|
|
|
target,
|
|
|
|
hydrate: ${String(!!hydrate)},
|
|
|
|
props: config.props,
|
|
|
|
intro: config.intro
|
|
|
|
}, config.options || {});
|
|
|
|
|
|
|
|
const component = new SvelteComponent(options);
|
|
|
|
|
|
|
|
const waitUntil = async (fn, ms = 500) => {
|
|
|
|
const start = new Date().getTime();
|
|
|
|
do {
|
|
|
|
if (fn()) return;
|
|
|
|
await new Promise(resolve => window.setTimeout(resolve, 1));
|
|
|
|
} while (new Date().getTime() <= start + ms);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (config.html) {
|
|
|
|
assert.htmlEqual(target.innerHTML, config.html);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config.test) {
|
|
|
|
await config.test({
|
|
|
|
assert,
|
|
|
|
component,
|
|
|
|
target,
|
|
|
|
window,
|
|
|
|
waitUntil,
|
|
|
|
});
|
|
|
|
|
|
|
|
component.$destroy();
|
|
|
|
|
|
|
|
if (unhandled_rejection) {
|
|
|
|
throw unhandled_rejection;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
component.$destroy();
|
|
|
|
assert.htmlEqual(target.innerHTML, '');
|
|
|
|
|
|
|
|
if (unhandled_rejection) {
|
|
|
|
throw unhandled_rejection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config.after_test) config.after_test();
|
|
|
|
} catch (error) {
|
|
|
|
if (config.error) {
|
|
|
|
assert.equal(err.message, config.error);
|
|
|
|
} else {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
window.removeEventListener('unhandledrejection', unhandled_rejection_handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
transform(code, id) {
|
|
|
|
if (id.endsWith('.svelte')) {
|
|
|
|
const compiled = svelte.compile(code.replace(/\r/g, ''), {
|
|
|
|
...config.compileOptions,
|
|
|
|
hydratable: hydrate,
|
|
|
|
immutable: config.immutable,
|
|
|
|
accessors: 'accessors' in config ? config.accessors : true
|
|
|
|
});
|
|
|
|
|
|
|
|
const out_dir = `${__dirname}/samples/${dir}/_output/${hydrate ? 'hydratable' : 'normal'}`;
|
|
|
|
const out = `${out_dir}/${path.basename(id).replace(/\.svelte$/, '.js')}`;
|
|
|
|
|
|
|
|
if (fs.existsSync(out)) {
|
|
|
|
fs.unlinkSync(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
mkdirp(out_dir);
|
|
|
|
fs.writeFileSync(out, compiled.js.code, 'utf8');
|
|
|
|
|
|
|
|
compiled.warnings.forEach(w => warnings.push(w));
|
|
|
|
|
|
|
|
return compiled.js;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
virtual({ assert })
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
const result = await bundle.generate({ format: 'iife', name: 'test' });
|
|
|
|
code = result.output[0].code;
|
|
|
|
|
|
|
|
function assertWarnings() {
|
|
|
|
if (config.warnings) {
|
|
|
|
deepEqual(warnings.map(w => ({
|
|
|
|
code: w.code,
|
|
|
|
message: w.message,
|
|
|
|
pos: w.pos,
|
|
|
|
start: w.start,
|
|
|
|
end: w.end
|
|
|
|
})), config.warnings);
|
|
|
|
} else if (warnings.length) {
|
|
|
|
failed.add(dir);
|
|
|
|
/* eslint-disable no-unsafe-finally */
|
|
|
|
throw new Error('Received unexpected warnings');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
browser = await executeBrowserTest(
|
|
|
|
browser,
|
|
|
|
launchPuppeteer,
|
|
|
|
assertWarnings,
|
|
|
|
(err) => {
|
|
|
|
failed.add(dir);
|
|
|
|
prettyPrintPuppeteerAssertionError(err.message);
|
|
|
|
assertWarnings();
|
|
|
|
});
|
|
|
|
}).timeout(is_first_run ? 20000 : 10000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Increase the timeout on the first run in preparation for restarting Chromium due to SIGSEGV.
|
|
|
|
let first_run = true;
|
|
|
|
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
|
|
|
|
runTest(dir, false, first_run);
|
|
|
|
runTest(dir, true, first_run);
|
|
|
|
first_run = false;
|
|
|
|
});
|
|
|
|
});
|