Compare commits

...

6 Commits

Author SHA1 Message Date
Rich Harris
e1926e1bcb -> v0.2.8 2017-12-23 13:16:58 -05:00
Rich Harris
db1c1f332a Add goto function (#46)
* WIP

* programmatic navigation

* get tests working

* wait longer
2017-12-23 13:15:40 -05:00
Rich Harris
e8d510b261 dont use /tmp, it fails when now server wakes after a sleep 2017-12-23 12:27:51 -05:00
Rich Harris
f8e237b265 make invalidation message look less like an error 2017-12-23 10:55:55 -05:00
Rich Harris
68c2f2e388 linting 2017-12-22 09:19:32 -05:00
Rich Harris
0bcb61650b use nightmare for testing 2017-12-21 16:48:53 -05:00
31 changed files with 2630 additions and 4584 deletions

View File

@@ -1,7 +1,6 @@
{ {
"root": true, "root": true,
"rules": { "rules": {
"indent": [ 2, "tab", { "SwitchCase": 1 } ],
"semi": [ 2, "always" ], "semi": [ 2, "always" ],
"space-before-blocks": [ 2, "always" ], "space-before-blocks": [ 2, "always" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ], "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
@@ -33,7 +32,7 @@
"plugin:import/warnings" "plugin:import/warnings"
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 6, "ecmaVersion": 8,
"sourceType": "module" "sourceType": "module"
} }
} }

View File

@@ -1,10 +1,21 @@
sudo: false sudo: false
language: node_js language: node_js
node_js: node_js:
- "stable" - "stable"
env: env:
global: global:
- BUILD_TIMEOUT=10000 - BUILD_TIMEOUT=10000
addons:
apt:
packages:
- xvfb
install: install:
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- npm install - npm install
- (cd test/app && npm install) - (cd test/app && npm install)

View File

@@ -1,5 +1,10 @@
# sapper changelog # sapper changelog
## 0.2.8
* Add `goto` function ([#29](https://github.com/sveltejs/sapper/issues/29))
* Don't use `/tmp` as destination in Now environments
## 0.2.7 ## 0.2.7
* Fix streaming bug * Fix streaming bug

View File

@@ -1,6 +1,5 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const glob = require('glob');
const mkdirp = require('mkdirp'); const mkdirp = require('mkdirp');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const { client, server } = require('./utils/compilers.js'); const { client, server } = require('./utils/compilers.js');
@@ -9,7 +8,7 @@ const generate_asset_cache = require('./utils/generate_asset_cache.js');
const { dest } = require('./config.js'); const { dest } = require('./config.js');
module.exports = () => { module.exports = () => {
mkdirp(dest); mkdirp.sync(dest);
rimraf.sync(path.join(dest, '**/*')); rimraf.sync(path.join(dest, '**/*'));
// create main.js and server-routes.js // create main.js and server-routes.js

View File

@@ -8,13 +8,10 @@ exports.templates = path.resolve(process.env.SAPPER_TEMPLATES || 'templates');
exports.src = path.resolve(process.env.SAPPER_ROUTES || 'routes'); exports.src = path.resolve(process.env.SAPPER_ROUTES || 'routes');
exports.dest = path.resolve( exports.dest = path.resolve(process.env.SAPPER_DEST || '.sapper');
process.env.NOW ? '/tmp' :
process.env.SAPPER_DEST || '.sapper'
);
if (exports.dev) { if (exports.dev) {
mkdirp(exports.dest); mkdirp.sync(exports.dest);
rimraf.sync(path.join(exports.dest, '**/*')); rimraf.sync(path.join(exports.dest, '**/*'));
} }

View File

@@ -1,9 +1,5 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const glob = require('glob');
const rimraf = require('rimraf');
const mkdirp = require('mkdirp');
const webpack = require('webpack');
const route_manager = require('./route_manager.js'); const route_manager = require('./route_manager.js');
const templates = require('./templates.js'); const templates = require('./templates.js');
const create_app = require('./utils/create_app.js'); const create_app = require('./utils/create_app.js');
@@ -11,7 +7,7 @@ const create_watcher = require('./utils/create_watcher.js');
const compilers = require('./utils/compilers.js'); const compilers = require('./utils/compilers.js');
const generate_asset_cache = require('./utils/generate_asset_cache.js'); const generate_asset_cache = require('./utils/generate_asset_cache.js');
const escape_html = require('escape-html'); const escape_html = require('escape-html');
const { src, dest, dev } = require('./config.js'); const { dest, dev } = require('./config.js');
function connect_dev() { function connect_dev() {
create_app(); create_app();
@@ -20,7 +16,7 @@ function connect_dev() {
let asset_cache; let asset_cache;
return compose_handlers([ const middleware = compose_handlers([
require('webpack-hot-middleware')(compilers.client, { require('webpack-hot-middleware')(compilers.client, {
reload: true, reload: true,
path: '/__webpack_hmr', path: '/__webpack_hmr',
@@ -59,6 +55,13 @@ function connect_dev() {
get_not_found_handler(() => asset_cache) get_not_found_handler(() => asset_cache)
]); ]);
middleware.close = () => {
watcher.close();
// TODO shut down chokidar
};
return middleware;
} }
function connect_prod() { function connect_prod() {
@@ -67,7 +70,7 @@ function connect_prod() {
read_json(path.join(dest, 'stats.server.json')) read_json(path.join(dest, 'stats.server.json'))
); );
return compose_handlers([ const middleware = compose_handlers([
set_req_pathname, set_req_pathname,
get_asset_handler({ get_asset_handler({
@@ -95,6 +98,12 @@ function connect_prod() {
get_not_found_handler(() => asset_cache) get_not_found_handler(() => asset_cache)
]); ]);
// here for API consistency between dev, and prod, but
// doesn't actually need to do anything
middleware.close = () => {};
return middleware;
} }
module.exports = dev ? connect_dev : connect_prod; module.exports = dev ? connect_dev : connect_prod;
@@ -139,7 +148,7 @@ function get_route_handler(fn) {
// TODO detect other stuff we can preload? images, CSS, fonts? // TODO detect other stuff we can preload? images, CSS, fonts?
res.set('Link', `<${client.main_file}>;rel="preload";as="script", <${client.routes[route.id]}>;rel="preload";as="script"`); res.set('Link', `<${client.main_file}>;rel="preload";as="script", <${client.routes[route.id]}>;rel="preload";as="script"`);
let data = { params: req.params, query: req.query }; const data = { params: req.params, query: req.query };
if (mod.preload) { if (mod.preload) {
const promise = Promise.resolve(mod.preload(data)).then(preloaded => { const promise = Promise.resolve(mod.preload(data)).then(preloaded => {
@@ -147,7 +156,7 @@ function get_route_handler(fn) {
return mod.render(data); return mod.render(data);
}); });
templates.stream(res, 200, { await templates.stream(res, 200, {
main: client.main_file, main: client.main_file,
html: promise.then(rendered => rendered.html), html: promise.then(rendered => rendered.html),
head: promise.then(({ head }) => `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`), head: promise.then(({ head }) => `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`),
@@ -178,28 +187,30 @@ function get_route_handler(fn) {
next(); next();
} catch(err) { } catch(err) {
res.status(500).end(templates.render(500, { res.status(500);
res.end(templates.render(500, {
title: (err && err.name) || 'Internal server error', title: (err && err.name) || 'Internal server error',
url, url,
error: escape_html(err && (err.details || err.message || err) || 'Unknown error'), error: escape_html(err && (err.details || err.message || err) || 'Unknown error'),
stack: err && err.stack.split('\n').slice(1).join('\n') stack: err && err.stack.split('\n').slice(1).join('\n')
})); }));
} }
} };
} }
function get_not_found_handler(fn) { function get_not_found_handler(fn) {
return function handle_not_found(req, res) { return function handle_not_found(req, res) {
const asset_cache = fn(); const asset_cache = fn();
res.status(404).end(templates.render(404, { res.status(404);
res.end(templates.render(404, {
title: 'Not found', title: 'Not found',
status: 404, status: 404,
method: req.method, method: req.method,
main: asset_cache.client.main_file, main: asset_cache.client.main_file,
url: req.url url: req.url
})); }));
} };
} }
function compose_handlers(handlers) { function compose_handlers(handlers) {
@@ -219,7 +230,7 @@ function compose_handlers(handlers) {
} }
go(); go();
} };
} }
function read_json(file) { function read_json(file) {

View File

@@ -21,7 +21,8 @@ update();
if (dev) { if (dev) {
const watcher = chokidar.watch(`${src}/**/*.+(html|js|mjs)`, { const watcher = chokidar.watch(`${src}/**/*.+(html|js|mjs)`, {
ignoreInitial: true ignoreInitial: true,
persistent: false
}); });
watcher.on('add', update); watcher.on('add', update);

View File

@@ -57,7 +57,7 @@ function create_templates() {
i = end + 1; i = end + 1;
} while (i < template.length); } while (i < template.length);
} }
} };
}) })
.sort((a, b) => b.specificity - a.specificity); .sort((a, b) => b.specificity - a.specificity);
} }
@@ -66,7 +66,8 @@ create_templates();
if (dev) { if (dev) {
const watcher = chokidar.watch('templates/**.html', { const watcher = chokidar.watch('templates/**.html', {
ignoreInitial: true ignoreInitial: true,
persistent: false
}); });
watcher.on('add', create_templates); watcher.on('add', create_templates);

View File

@@ -2,10 +2,10 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const route_manager = require('../route_manager.js'); const route_manager = require('../route_manager.js');
const { src, dest, entry, dev } = require('../config.js'); const { src, entry, dev } = require('../config.js');
function posixify(file) { function posixify(file) {
return file.replace(/[\/\\]/g, '/'); return file.replace(/[/\\]/g, '/');
} }
function create_app() { function create_app() {
@@ -71,7 +71,8 @@ if (dev) {
route_manager.onchange(create_app); route_manager.onchange(create_app);
const watcher = chokidar.watch(`templates/main.js`, { const watcher = chokidar.watch(`templates/main.js`, {
ignoreInitial: true ignoreInitial: true,
persistent: false
}); });
watcher.on('add', create_app); watcher.on('add', create_app);

View File

@@ -74,4 +74,4 @@ module.exports = function create_matchers(files) {
}); });
return routes; return routes;
} };

View File

@@ -38,15 +38,11 @@ module.exports = function create_watcher() {
); );
}); });
watcher = {
ready: invalidate()
};
function watch_compiler(type) { function watch_compiler(type) {
const compiler = compilers[type]; const compiler = compilers[type];
compiler.plugin('invalid', filename => { compiler.plugin('invalid', filename => {
console.log(chalk.red(`${type} bundle invalidated, file changed: ${chalk.bold(filename)}`)); console.log(chalk.cyan(`${type} bundle invalidated, file changed: ${chalk.bold(filename)}`));
deferreds[type] = deferred(); deferreds[type] = deferred();
watcher.ready = invalidate(); watcher.ready = invalidate();
}); });
@@ -55,7 +51,7 @@ module.exports = function create_watcher() {
deferreds[type].reject(err); deferreds[type].reject(err);
}); });
compiler.watch({}, (err, stats) => { return compiler.watch({}, (err, stats) => {
if (stats.hasErrors()) { if (stats.hasErrors()) {
deferreds[type].reject(stats.toJson().errors[0]); deferreds[type].reject(stats.toJson().errors[0]);
} else { } else {
@@ -64,8 +60,16 @@ module.exports = function create_watcher() {
}); });
} }
watch_compiler('client'); const watcher = {
watch_compiler('server'); ready: invalidate(),
client: watch_compiler('client'),
server: watch_compiler('server'),
close: () => {
watcher.client.close();
watcher.server.close();
}
};
return watcher; return watcher;
}; };

View File

@@ -1,2 +1,3 @@
--recursive --recursive
test/unit/**/*.js test/unit/**/*.js
test/common/test.js

3239
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.2.7", "version": "0.2.8",
"description": "Military-grade apps, engineered by Svelte", "description": "Military-grade apps, engineered by Svelte",
"main": "lib/index.js", "main": "lib/index.js",
"bin": { "bin": {
@@ -21,22 +21,23 @@
"webpack-hot-middleware": "^2.21.0" "webpack-hot-middleware": "^2.21.0"
}, },
"devDependencies": { "devDependencies": {
"cypress": "^1.3.0", "css-loader": "^0.28.7",
"eslint": "^4.13.1", "eslint": "^4.13.1",
"eslint-plugin-import": "^2.8.0",
"express": "^4.16.2",
"get-port": "^3.2.0",
"mocha": "^4.0.1", "mocha": "^4.0.1",
"nightmare": "^2.10.0",
"node-fetch": "^1.7.3",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"style-loader": "^0.19.1",
"svelte": "^1.49.1",
"svelte-loader": "^2.3.2",
"wait-on": "^2.0.2" "wait-on": "^2.0.2"
}, },
"scripts": { "scripts": {
"cy:open": "cypress open", "cy:open": "cypress open",
"test": "run-s test:unit test:dev test:prod", "test": "mocha --opts mocha.opts"
"test:unit": "mocha --opts mocha.opts",
"test:dev": "run-p --race test:launch:dev cy:run:dev",
"test:launch:dev": "node test/launch.js --dev",
"cy:run:dev": "wait-on http://localhost:3000 && cypress run -s test/cypress/integration/dev.js",
"test:prod": "run-p --race test:launch:prod cy:run:prod",
"test:launch:prod": "node test/launch.js --prod",
"cy:run:prod": "wait-on http://localhost:3000 && cypress run -s test/cypress/integration/prod.js"
}, },
"repository": "https://github.com/sveltejs/sapper", "repository": "https://github.com/sveltejs/sapper",
"keywords": [ "keywords": [

View File

@@ -6,12 +6,17 @@ export let component;
let target; let target;
let routes; let routes;
const history = typeof window !== 'undefined' ? window.history : {
pushState: () => {},
replaceState: () => {},
};
const scroll_history = {}; const scroll_history = {};
let uid = 1; let uid = 1;
let cid; let cid;
if ('scrollRestoration' in history) { if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual' history.scrollRestoration = 'manual';
} }
function select_route(url) { function select_route(url) {
@@ -39,7 +44,7 @@ function render(Component, data, scroll) {
} else { } else {
// first load — remove SSR'd <head> contents // first load — remove SSR'd <head> contents
const start = document.querySelector('#sapper-head-start'); const start = document.querySelector('#sapper-head-start');
let end = document.querySelector('#sapper-head-end'); const end = document.querySelector('#sapper-head-end');
if (start && end) { if (start && end) {
while (start.nextSibling !== end) detach(start.nextSibling); while (start.nextSibling !== end) detach(start.nextSibling);
@@ -75,8 +80,6 @@ function navigate(url, id) {
id = cid = ++uid; id = cid = ++uid;
scroll_history[cid] = { x: 0, y: 0 }; scroll_history[cid] = { x: 0, y: 0 };
history.pushState({ id }, '', url.href);
} }
selected.route.load().then(mod => { selected.route.load().then(mod => {
@@ -123,6 +126,7 @@ function handle_click(event) {
if (navigate(url, null)) { if (navigate(url, null)) {
event.preventDefault(); event.preventDefault();
history.pushState({ id: cid }, '', url.href);
} }
} }
@@ -173,7 +177,7 @@ export function init(_target, _routes) {
inited = true; inited = true;
} }
const scroll = scroll_history[uid] = scroll_state(); scroll_history[uid] = scroll_state();
history.replaceState({ id: uid }, '', window.location.href); history.replaceState({ id: uid }, '', window.location.href);
navigate(new URL(window.location), uid); navigate(new URL(window.location), uid);
@@ -189,4 +193,12 @@ function scroll_state() {
x: window.scrollX, x: window.scrollX,
y: window.scrollY y: window.scrollY
}; };
}
export function goto(href, opts = {}) {
if (navigate(new URL(href, window.location.href))) {
if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href);
} else {
window.location.href = href;
}
} }

1
test/app/.gitignore vendored
View File

@@ -3,3 +3,4 @@ node_modules
.sapper .sapper
yarn.lock yarn.lock
cypress/screenshots cypress/screenshots
templates/.*

View File

@@ -6,14 +6,21 @@
<h1>About this site</h1> <h1>About this site</h1>
<p>This is the 'about' page. There's not much here.</p> <p>This is the 'about' page. There's not much here.</p>
<button on:click='goto("/blog/what-is-sapper")'>What is Sapper?</button>
</Layout> </Layout>
<script> <script>
import Layout from './_components/Layout.html'; import Layout from './_components/Layout.html';
import { goto } from '../../../runtime/app.js';
export default { export default {
components: { components: {
Layout Layout
},
methods: {
goto
} }
}; };
</script> </script>

View File

@@ -10,9 +10,9 @@
<link rel='icon' type='image/png' href='/favicon.png'> <link rel='icon' type='image/png' href='/favicon.png'>
<script> <script>
if ('serviceWorker' in navigator) { // if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js'); // navigator.serviceWorker.register('/service-worker.js');
} // }
</script> </script>
<!-- Sapper generates a <style> tag containing critical CSS <!-- Sapper generates a <style> tag containing critical CSS

View File

@@ -1,10 +1,6 @@
import { init } from '__app__'; import { init } from '../../../runtime/app.js';
// `routes` is an array of route objects injected by Sapper // `routes` is an array of route objects injected by Sapper
init(document.querySelector('#sapper'), __routes__); init(document.querySelector('#sapper'), __routes__);
// if (__dev__) { window.READY = true;
// // Enable hot-module reloading
// import('sapper/webpack/hmr');
// if (module.hot) module.hot.accept();
// }

View File

@@ -1,7 +1,5 @@
const config = require('../../webpack/config.js'); const config = require('../../webpack/config.js');
const webpack = require('webpack'); const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
module.exports = { module.exports = {
entry: config.client.entry(), entry: config.client.entry(),
@@ -24,27 +22,18 @@ module.exports = {
} }
} }
}, },
config.dev && { {
test: /\.css$/, test: /\.css$/,
use: [ use: [
{ loader: "style-loader" }, { loader: "style-loader" },
{ loader: "css-loader" } { loader: "css-loader" }
] ]
},
!config.dev && {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{ loader: 'css-loader', options: { sourceMap: config.dev } }]
})
} }
].filter(Boolean) ].filter(Boolean)
}, },
plugins: [ plugins: [
config.dev && new webpack.HotModuleReplacementPlugin(), config.dev && new webpack.HotModuleReplacementPlugin(),
!config.dev && new ExtractTextPlugin('main.css'), !config.dev && new webpack.optimize.ModuleConcatenationPlugin()
!config.dev && new webpack.optimize.ModuleConcatenationPlugin(),
!config.dev && new UglifyJSPlugin()
].filter(Boolean), ].filter(Boolean),
devtool: config.dev ? 'inline-source-map' : false devtool: config.dev ? 'inline-source-map' : false
}; };

View File

@@ -1,7 +1,4 @@
const config = require('../../webpack/config.js'); const config = require('../../webpack/config.js');
const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
module.exports = { module.exports = {
entry: config.server.entry(), entry: config.server.entry(),

234
test/common/test.js Normal file
View File

@@ -0,0 +1,234 @@
const path = require('path');
const assert = require('assert');
const express = require('express');
const serve = require('serve-static');
const Nightmare = require('nightmare');
const getPort = require('get-port');
const fetch = require('node-fetch');
run('production');
run('development');
function run(env) {
describe(`env=${env}`, function () {
this.timeout(5000);
let PORT;
let server;
let nightmare;
let middleware;
let capture;
let base;
function get(url) {
return new Promise(fulfil => {
const req = {
url,
method: 'GET'
};
const result = {
headers: {},
body: ''
};
const res = {
set: (headers, value) => {
if (typeof headers === 'string') {
return res.set({ [headers]: value });
}
Object.assign(result.headers, headers);
},
status: code => {
result.status = code;
},
write: data => {
result.body += data;
},
end: data => {
result.body += data;
fulfil(result);
}
};
middleware(req, res, () => {
fulfil(result);
});
});
}
before(async () => {
process.chdir(path.resolve(__dirname, '../app'));
process.env.NODE_ENV = env;
if (env === 'production') {
const cli = path.resolve(__dirname, '../../cli/index.js');
await exec(`${cli} build`);
}
const resolved = require.resolve('../..');
delete require.cache[resolved];
const sapper = require(resolved);
PORT = await getPort();
base = `http://localhost:${PORT}`;
global.fetch = (url, opts) => {
if (url[0] === '/') url = `${base}${url}`;
return fetch(url, opts);
};
let captured;
capture = async fn => {
const result = captured = [];
await fn();
captured = null;
return result;
};
const app = express();
app.use(serve('assets'));
app.use((req, res, next) => {
if (captured) captured.push(req);
next();
});
middleware = sapper();
app.use(middleware);
return new Promise((fulfil, reject) => {
server = app.listen(PORT, err => {
if (err) reject(err);
else fulfil();
});
});
});
after(() => {
server.close();
middleware.close();
// give a chance to clean up
return new Promise(fulfil => setTimeout(fulfil, 500));
});
describe('basic functionality', () => {
beforeEach(() => {
nightmare = new Nightmare();
nightmare.on('console', (type, ...args) => {
console[type](...args);
});
nightmare.on('page', (type, ...args) => {
if (type === 'error') {
console.error(args[1]);
} else {
console.warn(type, args);
}
});
});
afterEach(async () => {
await nightmare.end();
});
it('serves /', async () => {
const title = await nightmare
.goto(base)
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'Great success!');
});
it('serves static route', async () => {
const title = await nightmare
.goto(`${base}/about`)
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'About this site');
});
it('serves dynamic route', async () => {
const title = await nightmare
.goto(`${base}/blog/what-is-sapper`)
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'What is Sapper?');
});
it('navigates to a new page without reloading', async () => {
await nightmare.goto(base);
const requests = await capture(async () => {
await nightmare.click('a[href="/about"]');
});
assert.equal(
await nightmare.path(),
'/about'
);
assert.equal(
await nightmare.evaluate(() => document.title),
'About'
);
assert.deepEqual(requests.map(r => r.url), []);
});
it('navigates programmatically', async () => {
await nightmare
.goto(`${base}/about`)
.wait(() => window.READY)
.click('button')
.wait(() => window.location.pathname === '/blog/what-is-sapper')
.wait(100);
assert.equal(
await nightmare.evaluate(() => document.title),
'What is Sapper?'
);
});
});
describe('headers', () => {
it('sets Content-Type and Link...preload headers', async () => {
const { headers } = await get('/');
assert.equal(
headers['Content-Type'],
'text/html'
);
assert.ok(
/<\/client\/main.\w+\.js>;rel="preload";as="script", <\/client\/_.\d+.\w+.js>;rel="preload";as="script"/.test(headers['Link']),
headers['Link']
);
});
});
});
}
function exec(cmd) {
return new Promise((fulfil, reject) => {
require('child_process').exec(cmd, (err, stdout, stderr) => {
if (err) {
process.stdout.write(stdout);
process.stderr.write(stderr);
return reject(err);
}
fulfil();
});
});
}

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -1,19 +0,0 @@
describe('dev mode', () => {
beforeEach(() => {
cy.visit('/')
});
it('has the correct <h1>', () => {
cy.contains('h1', 'Great success!')
});
it('navigates to /about', () => {
cy.get('nav a').contains('about').click();
cy.url().should('include', '/about');
});
it('navigates to /blog', () => {
cy.get('nav a').contains('blog').click();
cy.url().should('include', '/blog');
});
});

View File

@@ -1,19 +0,0 @@
describe('prod mode', () => {
beforeEach(() => {
cy.visit('/')
});
it('has the correct <h1>', () => {
cy.contains('h1', 'Great success!')
});
it('navigates to /about', () => {
cy.get('nav a').contains('about').click();
cy.url().should('include', '/about');
});
it('navigates to /blog', () => {
cy.get('nav a').contains('blog').click();
cy.url().should('include', '/blog');
});
});

View File

@@ -1,17 +0,0 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

View File

@@ -1,25 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

View File

@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1,75 +0,0 @@
const fs = require('fs');
const path = require('path');
const rimraf = require('rimraf');
const child_process = require('child_process');
// ensure sapper doesn't exist in app/node_modules
rimraf.sync(
path.join(__dirname, 'app/node_modules/sapper')
);
rimraf.sync(
path.join(__dirname, 'app/node_modules/.bin/sapper')
);
// create symlinks
fs.symlinkSync(
path.join(__dirname, '..'),
path.join(__dirname, 'app/node_modules/sapper')
);
fs.symlinkSync(
path.join(__dirname, '../cli/index.js'),
path.join(__dirname, 'app/node_modules/.bin/sapper')
);
const app_dir = path.join(__dirname, 'app');
function start_server() {
const server = child_process.spawn(process.execPath, ['server.js'], {
cwd: app_dir,
env: {
NODE_ENV: 'development'
},
stdio: 'pipe'
});
server.stdout.on('data', (data) => {
process.stdout.write(data);
});
server.stderr.on('data', (data) => {
process.stderr.write(data);
});
}
function launch() {
if (process.argv[2] === '--dev') {
start_server();
} else {
child_process.exec(`npm run build`, {
cwd: app_dir
}, (err, stdout, stderr) => {
if (err) throw err;
process.stdout.write(stdout);
process.stderr.write(stderr);
start_server();
});
}
}
// this is a terrible hack
if (process.env.APPVEYOR) {
child_process.exec(`npm install`, {
cwd: app_dir
}, (err, stdout, stderr) => {
if (err) throw err;
process.stdout.write(stdout);
process.stderr.write(stderr);
launch();
});
} else {
launch();
}

3370
yarn.lock

File diff suppressed because it is too large Load Diff