Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ jobs:
<<: *job_defaults
steps:
- *attach_workspace
- *restore_cache
- run:
name: Integration Tests
command: yarn test:ci:integration:ssr
Expand Down
18 changes: 8 additions & 10 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@
"main": "./integration/main.browser.ts",
"tsConfig": "./integration/tsconfig.app.json",
"polyfills": "./integration/polyfills.ts",
"assets": ["./integration/favicon.ico"],
"assets": [
{
"glob": "favicon.ico",
"input": "./integration",
"output": "./"
}
],
"styles": ["./integration/styles.scss"]
},
"configurations": {
Expand Down Expand Up @@ -83,17 +89,9 @@
"tsConfig": "./integration/tsconfig.server.json"
},
"configurations": {
"dev": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": true
},
"production": {
"optimization": true,
"outputHashing": "all",
"outputHashing": "none",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
Expand Down
64 changes: 64 additions & 0 deletions cypress/ssr/ssr.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// <reference types="cypress" />

describe('Server side rendering', () => {
const listUrl = 'http://localhost:4200/list';
const faviconUrl = 'http://localhost:4200/favicon.ico';

it('should make sure the Express server is running', () => {
// Arrange & act
cy.request(listUrl)
.its('headers')
.then(headers => {
// Assert
expect(headers).to.have.property('x-powered-by');
});
});

it('should serve statics and favicon.ico', () => {
// Arrange & act & assert
cy.request(faviconUrl)
.its('status')
.should('equal', 200);
});

it('"ngOnInit todo" should exist', () => {
// Arrange & act & assert
cy.request(listUrl)
.its('body')
.should('include', 'ngOnInit todo');
});

it('lifecycle hooks should exist in the correct order (root => lazy)', () => {
// Arrange & act
cy.request(listUrl).then(({ body }) => {
const ngxsOnInitIndex = body.indexOf('NgxsOnInit todo');
const ngxsAfterBootstrapIndex = body.indexOf('NgxsAfterBootstrap todo');
const ngxsOnInitLazyIndex = body.indexOf('NgxsOnInit lazy');
const ngxsAfterBootstrapLazyIndex = body.indexOf('NgxsAfterBootstrap lazy');
const stringIndexes = [
ngxsOnInitIndex,
ngxsAfterBootstrapIndex,
ngxsOnInitLazyIndex,
ngxsAfterBootstrapLazyIndex
];

stringIndexes.forEach((stringIndex, index) => {
// Assert
expect(stringIndex).to.be.greaterThan(-1);
// If it's not the first in the array
// every next index should more than previous
if (index) {
expect(stringIndex).to.be.greaterThan(stringIndexes[index - 1]);
}
});
});
});

it('should successfully resolve list of animals', () => {
// Arrange & act & assert
cy.request(listUrl)
.its('body')
.should('include', 'animals were resolved')
.should('include', 'zebras,pandas,lions,giraffes');
});
});
3 changes: 1 addition & 2 deletions integration/app/app.browser.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import { TODOS_STORAGE_KEY } from '@integration/store/todos/todos.model';
BrowserAnimationsModule,
BrowserTransferStateModule,
NgxsStoragePluginModule.forRoot({ key: [TODOS_STORAGE_KEY] })
],
providers: [{ provide: 'ORIGIN_URL', useValue: location.origin }]
]
})
export class AppBrowserModule implements NgxsHmrLifeCycle<Snapshot> {
public hmrNgxsStoreOnInit(ctx: StateContext<Snapshot>, snapshot: Partial<Snapshot>) {
Expand Down
Binary file modified integration/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion integration/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="icon" type="image/x-icon" href="integration/favicon.ico">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body>
Expand Down
12 changes: 0 additions & 12 deletions integration/request-service.js

This file was deleted.

96 changes: 26 additions & 70 deletions integration/server.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,48 @@
import 'zone.js/dist/zone-node';
import 'reflect-metadata';

const fs = require('fs');
const path = require('path');
const files: string[] = fs.readdirSync(`./../dist-integration-server`);
import { join } from 'path';
import { readFileSync } from 'fs';
import * as express from 'express';

import { enableProdMode } from '@angular/core';
import * as express from 'express';
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
import { renderModuleFactory } from '@angular/platform-server';
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

const mainFiles = files.filter(file => file.startsWith('main'));
const hash = mainFiles[0].split('.')[1];
const {
AppServerModuleNgFactory,
LAZY_MODULE_MAP
} = require(`./../dist-integration-server/main.${hash}`);
import { ngExpressEngine } from '@nguniversal/express-engine';
import { exit } from 'process';
const PORT = process.env.PORT || 4000;
} = require('../dist-integration-server/main');

const PORT = process.env.PORT || 4200;
const DIST_FOLDER = join(__dirname, '../dist-integration');

enableProdMode();

const app = express();
app.use((req, res, next) => {
console.log(req.url);
if (req.url === '/robots.txt') {
return;
}

if (req.url === '/integration/favicon.ico') {
return;
}

if (req.url === '/test/exit') {
res.send('exit');
exit(0);
return;
}
next();
});
// Read `index.html` only once and cache it
const document = readFileSync(join(DIST_FOLDER, 'index.html')).toString();

type NgExpressEngines = (
path: string,
options: object,
callback: (e: any, rendered: string) => void
) => void;
// `index: false` means to ignore plain `index.html` thus
// the render responsibility is fully taken by Angular Universal
app.use(express.static(DIST_FOLDER, { index: false }));

app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)]
}) as NgExpressEngines);

app.set('view engine', 'html');
app.set('views', '.');

app.get('*.*', express.static(path.join(__dirname, '..', 'dist-integration')));

app.get('*', (req, res) => {
const http =
req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];

const url = req.originalUrl;
app.get('*', async (req, res) => {
const url = req.url;
// tslint:disable-next-line:no-console
console.time(`GET: ${url}`);
res.render(
'../dist-integration/index',
{
req: req,
res: res,
providers: [
{
provide: 'ORIGIN_URL',
useValue: `${http}://${req.headers.host}`
}
]
},
(err, html) => {
if (!!err) {
throw err;
}

// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
}
);
const html = await renderModuleFactory(AppServerModuleNgFactory, {
url,
document,
extraProviders: [provideModuleMap(LAZY_MODULE_MAP)]
});

// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
});

app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
console.log(`Express server is running and listening at http://localhost:${PORT}!`);
});
14 changes: 0 additions & 14 deletions integration/test.ssr.ts

This file was deleted.

7 changes: 0 additions & 7 deletions integration/tests-ssr/exit.mocha.ts

This file was deleted.

12 changes: 0 additions & 12 deletions integration/tests-ssr/request-service.js

This file was deleted.

43 changes: 0 additions & 43 deletions integration/tests-ssr/todo.mocha.ts

This file was deleted.

3 changes: 1 addition & 2 deletions integration/tsconfig.server.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"paths": {
"@ngxs/*": ["../@ngxs/*"],
Expand All @@ -11,7 +10,7 @@
"types": ["node"],
"typeRoots": ["../node_modules/@types"]
},
"exclude": ["test.ssr.ts", "**/*.spec.ts", "**/*.mocha.ts", "node_modules"],
"exclude": ["**/*.spec.ts", "node_modules"],
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
*/
globals: {
'ts-jest': {
tsConfig: '<rootDir>/tsconfig.json',
tsConfig: '<rootDir>/tsconfig.spec.json',
allowSyntheticDefaultImports: true
}
},
Expand Down
Loading