Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Change server context shape, make SSRBodyTemplate optional #294

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 1 addition & 49 deletions src/__tests__/app.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,17 @@ import test from 'tape-cup';
import App from '../index';
import {compose} from '../compose.js';

import type {Context} from '../types.js';

test('context composition', async t => {
const element = 'hello';
const render = el => `<h1>${el}</h1>`;
const wrap = (ctx, next) => {
ctx.element = ctx.element.toUpperCase();
return next();
};
const chunkUrlMap = new Map();
const chunkIdZero = new Map();
chunkIdZero.set('es5', 'es5-file.js');
chunkUrlMap.set(0, chunkIdZero);

const context = {
headers: {accept: 'text/html'},
path: '/',
syncChunks: [0],
preloadChunks: [],
chunkUrlMap,
webpackPublicPath: '/',
element: null,
rendered: null,
render: null,
Expand All @@ -53,42 +44,3 @@ test('context composition', async t => {
}
t.end();
});

test('context composition with a cdn', async t => {
const element = 'hello';
const render = el => `<h1>${el}</h1>`;
const wrap = () => (ctx: Context, next: () => Promise<void>) => {
ctx.element = ctx.element.toUpperCase();
return next();
};
const chunkUrlMap = new Map();
const chunkIdZero = new Map();
chunkIdZero.set('es5', 'es5-file.js');
chunkUrlMap.set(0, chunkIdZero);
const context = {
headers: {accept: 'text/html'},
path: '/',
syncChunks: [0],
preloadChunks: [],
chunkUrlMap,
webpackPublicPath: 'https://something.com/lol',
element: null,
rendered: null,
render: null,
type: null,
body: null,
};

const app = new App(element, render);
app.middleware(wrap());
app.resolve();
const middleware = compose(app.plugins);
try {
await middleware(((context: any): Context), () => Promise.resolve());
// $FlowFixMe
t.ok(context.body.includes('https://something.com/lol/es5-file.js'));
} catch (e) {
t.ifError(e, 'something went wrong');
}
t.end();
});
46 changes: 11 additions & 35 deletions src/__tests__/env.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,58 +12,34 @@ import {loadEnv} from '../get-env.js';
tape('loadEnv defaults', t => {
const env = loadEnv()();
t.deepEqual(env, {
rootDir: '.',
env: 'development',
prefix: '',
assetPath: '/_static',
baseAssetPath: '/_static',
cdnUrl: '',
webpackPublicPath: '/_static',
prefix: void 0,
cdnUrl: void 0,
});
t.end();
});

tape('loadEnv overrides', t => {
process.env.ROOT_DIR = 'test_root_dir';
process.env.NODE_ENV = 'production';
process.env.ROUTE_PREFIX = 'test_route_prefix';
process.env.FRAMEWORK_STATIC_ASSET_PATH = '/test_framework';
process.env.CDN_URL = 'test_cdn_url';
process.env.ROUTE_PREFIX = '/test_route_prefix';
process.env.CDN_URL = 'https://cdn.com';

const env = loadEnv()();
t.deepEqual(env, {
rootDir: 'test_root_dir',
env: 'production',
prefix: 'test_route_prefix',
assetPath: 'test_route_prefix/test_framework',
baseAssetPath: '/test_framework',
cdnUrl: 'test_cdn_url',
webpackPublicPath: 'test_cdn_url',
prefix: '/test_route_prefix',
cdnUrl: 'https://cdn.com',
});

process.env.ROOT_DIR = '';
process.env.NODE_ENV = '';
process.env.ROUTE_PREFIX = '';
process.env.FRAMEWORK_STATIC_ASSET_PATH = '';
process.env.CDN_URL = '';
delete process.env.ROUTE_PREFIX;
delete process.env.CDN_URL;
t.end();
});

tape('loadEnv validation', t => {
process.env.NODE_ENV = 'LOL';
t.throws(loadEnv, /Invalid NODE_ENV loaded/);
process.env.NODE_ENV = '';

process.env.ROUTE_PREFIX = 'test/';
t.throws(loadEnv, /ROUTE_PREFIX must not end with /);
process.env.ROUTE_PREFIX = '';

process.env.FRAMEWORK_STATIC_ASSET_PATH = 'test/';
t.throws(loadEnv, /FRAMEWORK_STATIC_ASSET_PATH must not end with /);
process.env.FRAMEWORK_STATIC_ASSET_PATH = '';
delete process.env.ROUTE_PREFIX;

process.env.CDN_URL = 'test/';
process.env.CDN_URL = 'https://cdn.com/test/';
t.throws(loadEnv, /CDN_URL must not end with /);
process.env.CDN_URL = '';
delete process.env.CDN_URL;
t.end();
});
124 changes: 20 additions & 104 deletions src/__tests__/index.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*/

import test from 'tape-cup';
import App, {html} from '../index';
import App from '../index';
import {run} from './test-helper';
import {SSRDeciderToken} from '../tokens';
import {SSRDeciderToken, SSRBodyTemplateToken} from '../tokens';
import {createPlugin} from '../create-plugin';
import BaseApp from '../base-app';

Expand Down Expand Up @@ -67,8 +67,8 @@ test('ssr with accept header', async t => {
// $FlowFixMe
const ctx = await run(app);
t.equals(typeof ctx.rendered, 'string', 'ctx.rendered');
t.equals(typeof ctx.body, 'string', 'renders ctx.body to string');
t.ok(!ctx.body.includes(element), 'does not renders element into ctx.body');
// t.equals(typeof ctx.body, 'string', 'renders ctx.body to string');
// t.ok(!ctx.body.includes(element), 'does not renders element into ctx.body');
t.ok(flags.render, 'calls render');
} catch (e) {
t.ifError(e, 'should not error');
Expand Down Expand Up @@ -203,17 +203,7 @@ test('disable SSR by composing SSRDecider with a function', async t => {
t.end();
});

test('SSR extension handling', async t => {
const extensionToSSRSupported = {
js: false,
gif: false,
jpg: false,
png: false,
pdf: false,
json: false,
html: true,
};

test('no SSR for asset paths', async t => {
const flags = {render: false};
const element = 'hi';
const render = () => {
Expand All @@ -226,20 +216,13 @@ test('SSR extension handling', async t => {
}

try {
for (let i in extensionToSSRSupported) {
flags.render = false;
let initialCtx = {
path: `/some-path.${i}`,
};
// $FlowFixMe
await run(buildApp(), initialCtx);
const shouldSSR = extensionToSSRSupported[i];
t.equals(
flags.render,
shouldSSR,
`extension of ${i} should ${shouldSSR ? '' : 'not'} have ssr`
);
}
flags.render = false;
let initialCtx = {
path: `/_static/foo`,
};
// $FlowFixMe
await run(buildApp(), initialCtx);
t.equals(flags.render, false, `request to static asset dir should not ssr`);
} catch (e) {
t.ifError(e, 'does not error');
}
Expand Down Expand Up @@ -374,94 +357,27 @@ test('SSR with redirects upstream', async t => {
t.end();
});

test('HTML escaping works', async t => {
test('SSRBodyTemplate is used', async t => {
const element = 'hi';
const render = el => el;
const template = (ctx, next) => {
ctx.template.htmlAttrs = {lang: '">'};
// $FlowFixMe
ctx.template.bodyAttrs = {test: '">'};
ctx.template.title = '</title>';
return next();
};
const app = new App(element, render);
app.middleware(template);

try {
// $FlowFixMe
const ctx = await run(app);
t.ok(ctx.body.includes('<html lang="\\u0022\\u003E">'), 'lang works');
t.ok(ctx.body.includes('<body test="\\u0022\\u003E">'), 'bodyAttrs work');
t.ok(
ctx.body.includes('<title>\\u003C\\u002Ftitle\\u003E</title>'),
'title works'
);
} catch (e) {
t.ifError(e, 'does not error');
}
t.end();
});
let called = false;
app.register(SSRBodyTemplateToken, ctx => {
called = true;
return `<html>${ctx.rendered}</html>`;
});

test('head and body must be sanitized', async t => {
const element = 'hi';
const render = el => el;
const template = (ctx, next) => {
ctx.template.head.push(html`<meta charset="${'">'}" />`);
ctx.template.body.push(html`<div>${'">'}</div>`);
return next();
};
const app = new App(element, render);
app.middleware(template);
try {
// $FlowFixMe
const ctx = await run(app);
t.ok(ctx.body.includes('<meta charset="\\u0022\\u003E" />'), 'head works');
t.ok(ctx.body.includes('<div>\\u0022\\u003E</div>'), 'body works');
t.equal(ctx.body, `<html>hi</html>`);
t.ok(called, 'ssrBodyTemplate called');
} catch (e) {
t.ifError(e, 'does not error');
}
t.end();
});

test('throws if head is not sanitized', async t => {
const element = 'hi';
const render = el => el;
const template = (ctx, next) => {
// $FlowFixMe
ctx.template.head.push(`<meta charset="${'">'}" />`);
return next();
};
const app = new App(element, render);
app.middleware(template);
try {
await run(app);
t.fail('should throw');
} catch (e) {
t.ok(e, 'throws if head is not sanitized');
}
t.end();
});

test('throws if body is not sanitized', async t => {
const element = 'hi';
const render = el => el;
const template = (ctx, next) => {
// $FlowFixMe
ctx.template.body.push(`<meta charset="${'">'}" />`);
return next();
};
const app = new App(element, render);
app.middleware(template);

try {
await run(app);
t.fail('should throw');
} catch (e) {
t.ok(e, 'throws if body is not sanitized');
}
t.end();
});

test('rendering error handling', async t => {
const element = 'hi';
const render = () => {
Expand Down
10 changes: 2 additions & 8 deletions src/base-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@

import {createPlugin} from './create-plugin';
import {createToken, TokenType, TokenImpl} from './create-token';
import {
ElementToken,
RenderToken,
SSRDeciderToken,
SSRBodyTemplateToken,
} from './tokens';
import {SSRDecider, SSRBodyTemplate} from './plugins/ssr';
import {ElementToken, RenderToken, SSRDeciderToken} from './tokens';

import type {aliaser, cleanupFn, FusionPlugin, Token} from './types.js';
import {SSRDecider} from './plugins/ssr';

class FusionApp {
constructor(el: Element | string, render: *) {
Expand All @@ -27,7 +22,6 @@ class FusionApp {
el && this.register(ElementToken, el);
render && this.register(RenderToken, render);
this.register(SSRDeciderToken, SSRDecider);
this.register(SSRBodyTemplateToken, SSRBodyTemplate);
}

// eslint-disable-next-line
Expand Down
15 changes: 1 addition & 14 deletions src/flow/flow-fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,23 +127,10 @@ async function cleanup() {

/* - Case: getEnv typing */
async function checkEnv() {
const {
rootDir,
env,
prefix,
assetPath,
baseAssetPath,
cdnUrl,
webpackPublicPath,
} = getEnv();
const {prefix, cdnUrl} = getEnv();
return {
rootDir,
env,
prefix,
assetPath,
baseAssetPath,
cdnUrl,
webpackPublicPath,
};
}

Expand Down
Loading