Skip to content

Commit 555ffc3

Browse files
committed
feat: make e2e tests optional and restore linting
1 parent afd59bc commit 555ffc3

11 files changed

Lines changed: 321 additions & 2 deletions

File tree

generators/app/templates/_README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ dist/ mobile app production build
2727
dist/ web app production build
2828
<% } -%>
2929
docs/ project docs and coding guides
30+
<% if (props.e2e) { -%>
31+
e2e/ end-to-end tests
32+
<% } -%>
3033
src/ project source code
3134
|- app/ app components
3235
| |- core/ core module (singleton services and single-use components)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @ts-check
2+
// Protractor configuration file, see link for more information
3+
// https://github.com/angular/protractor/blob/master/lib/config.ts
4+
5+
const { SpecReporter } = require('jasmine-spec-reporter');
6+
7+
exports.config = {
8+
SELENIUM_PROMISE_MANAGER: false,
9+
allScriptsTimeout: 11000,
10+
specs: [
11+
'./src/**/*.e2e-spec.ts'
12+
],
13+
capabilities: {
14+
browserName: process.env.PROTRACTOR_BROWSER || 'chrome',
15+
chromeOptions: {
16+
binary: process.env.PROTRACTOR_CHROME_BIN || undefined,
17+
args: process.env.PROTRACTOR_CHROME_ARGS ? JSON.parse(process.env.PROTRACTOR_CHROME_ARGS) : ['lang=en-US'],
18+
prefs: {
19+
intl: { accept_languages: 'en-US' }
20+
}
21+
}
22+
},
23+
// Only works with Chrome and Firefox
24+
directConnect: true,
25+
baseUrl: 'http://localhost:4200/',
26+
framework: 'jasmine2',
27+
jasmineNodeOpts: {
28+
showColors: true,
29+
defaultTimeoutInterval: 30000,
30+
print: function() {}
31+
},
32+
onPrepare() {
33+
require('ts-node').register({
34+
project: require('path').join(__dirname, './tsconfig.json')
35+
});
36+
37+
// Better console spec reporter
38+
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: 'pretty' } }));
39+
}
40+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { browser, ExpectedConditions as until } from 'protractor';
2+
<% if (props.auth) { -%>
3+
import { LoginPage } from './page-objects/login.po';
4+
<% } -%>
5+
import { AppSharedPage } from './page-objects/app-shared.po';
6+
import { ShellPage } from './page-objects/shell.po';
7+
8+
describe('when the app loads', () => {
9+
<% if (props.auth) { -%>
10+
const login = new LoginPage();
11+
<% } -%>
12+
const app = new AppSharedPage();
13+
const shell = new ShellPage();
14+
15+
beforeAll(async () => {
16+
await app.navigateAndSetLanguage();
17+
});
18+
19+
<% if (props.auth) { -%>
20+
it('should display the login page', async () => {
21+
expect(await browser.getCurrentUrl()).toContain('/login');
22+
});
23+
24+
<% } else { -%>
25+
it('should display the shell page', async () => {
26+
expect(await browser.getCurrentUrl()).toContain('/');
27+
});
28+
29+
<% } -%>
30+
<% if (props.auth) { -%>
31+
describe('and the user logs in', () => {
32+
beforeAll(async () => {
33+
await login.login();
34+
});
35+
36+
<% } else { -%>
37+
describe('and the page loads', () => {
38+
<% } -%>
39+
it('should display the hello message', async () => {
40+
await browser.wait(until.visibilityOf(shell.welcomeText), 5000, 'Element taking too long to appear');
41+
expect(await shell.getParagraphText()).toEqual('Hello world !');
42+
});
43+
});
44+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Use the Page Object pattern to define the page under test.
3+
* See docs/coding-guide/e2e-tests.md for more info.
4+
*/
5+
6+
import { browser, element, by } from 'protractor';
7+
8+
export class LoginPage {
9+
<% if (props.ui === 'ionic') { -%>
10+
// Custom locator to find an element within shadow DOM (by.deepCss() doesn't work here)
11+
usernameField = element(by.css('ion-input[formControlName="username"] input'));
12+
passwordField = element(by.css('ion-input[formControlName="password"] input'));
13+
loginButton = element(by.css('ion-button[type="submit"]'));
14+
<% } else { -%>
15+
usernameField = element(by.css('input[formControlName="username"]'));
16+
passwordField = element(by.css('input[formControlName="password"]'));
17+
loginButton = element(by.css('button[type="submit"]'));
18+
<% } -%>
19+
20+
async login() {
21+
await this.usernameField.sendKeys('test');
22+
await this.passwordField.sendKeys('123');
23+
await this.loginButton.click();
24+
}
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Use the Page Object pattern to define the page under test.
3+
* See docs/coding-guide/e2e-tests.md for more info.
4+
*/
5+
6+
import { browser, element, by } from 'protractor';
7+
8+
export class AppSharedPage {
9+
async navigateAndSetLanguage() {
10+
// Forces default language
11+
await this.navigateTo();
12+
await browser.executeScript(() => localStorage.setItem('language', 'en-US'));
13+
}
14+
15+
async navigateTo() {
16+
await browser.get('/');
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Use the Page Object pattern to define the page under test.
3+
* See docs/coding-guide/e2e-tests.md for more info.
4+
*/
5+
6+
import { browser, element, by } from 'protractor';
7+
8+
export class ShellPage {
9+
<% if (props.ui === 'ionic') { -%>
10+
welcomeText = element(by.css('app-root ion-card-title'));
11+
<% } else if (props.ui === 'material') { -%>
12+
welcomeText = element(by.css('app-root mat-card-title'));
13+
<% } else { -%>
14+
welcomeText = element(by.css('app-root h1'));
15+
<% } -%>
16+
17+
getParagraphText() {
18+
return this.welcomeText.getText();
19+
}
20+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
2+
{
3+
"extends": "../tsconfig.json",
4+
"compilerOptions": {
5+
"outDir": "../out-tsc/e2e",
6+
"module": "commonjs",
7+
"target": "es2018",
8+
"types": [
9+
"jasmine",
10+
"jasminewd2",
11+
"node"
12+
]
13+
}
14+
}

generators/app/templates/_angular.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,35 @@
156156
"watch": false
157157
}
158158
}
159+
<% } -%>
160+
},
161+
"lint": {
162+
"builder": "@angular-devkit/build-angular:tslint",
163+
"options": {
164+
"tsConfig": [
165+
<% if (props.e2e) { -%>
166+
"e2e/tsconfig.json",
167+
<% } -%>
168+
"tsconfig.app.json",
169+
"tsconfig.spec.json"
170+
],
171+
"exclude": [
172+
"**/node_modules/**"
173+
]
174+
}
175+
<% if (props.e2e) { -%>
176+
},
177+
"e2e": {
178+
"builder": "@angular-devkit/build-angular:protractor",
179+
"options": {
180+
"protractorConfig": "e2e/protractor.conf.js",
181+
"devServerTarget": "<%= props.projectName %>:serve"
182+
},
183+
"configurations": {
184+
"production": {
185+
"devServerTarget": "<%= props.projectName %>:serve:production"
186+
}
187+
}
159188
<% } -%>
160189
}
161190
}

generators/app/templates/_package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
<% if (props.pwa) { -%>
1717
"serve:sw": "<%= run('build') %> && npx http-server ./<%= props.target.includes('cordova') ? 'www': 'dist' %> -p 4200",
1818
<% } -%>
19-
"lint": "stylelint \"src/**/*.scss\" --syntax scss && htmlhint \"src\" --config .htmlhintrc",
19+
"lint": "ng lint && stylelint \"src/**/*.scss\" --syntax scss && htmlhint \"src\" --config .htmlhintrc",
2020
"test": "<%= run('write:env') %> && ng test",
2121
"test:ci": "<%= run('write:env') %> && <%= run('lint') %> && ng test --configuration=ci",
22+
<% if (props.e2e) { -%>
23+
"e2e": "<%= run('write:env') %> && ng e2e",
24+
<% } -%>
2225
"translations:extract": "ngx-translate-extract --input ./src --output ./src/translations/template.json --format=json --clean --sort",
2326
<% if (props.tools.includes('hads')) { -%>
2427
"docs": "hads ./docs -o",
@@ -174,12 +177,16 @@
174177
<% } -%>
175178
<% if (props.tools.includes('prettier')) { -%>
176179
"prettier": "^2.2.1",
180+
"tslint-config-prettier": "^1.18.0",
177181
"stylelint-config-prettier": "^8.0.2",
178182
<% if (options.git) { -%>
179183
"pretty-quick": "^3.1.0",
180184
"husky": "^4.3.6",
181185
<% } -%>
182186
<% } -%>
187+
<% if (props.e2e) { -%>
188+
"protractor": "~7.0.0",
189+
<% } -%>
183190
<% if (props.tools.includes('puppeteer')) { -%>
184191
"puppeteer": "^5.5.0",
185192
<% } -%>
@@ -188,6 +195,7 @@
188195
"stylelint-config-standard": "~20.0.0",
189196
"stylelint-scss": "~3.18.0",
190197
"ts-node": "^9.1.1",
198+
"tslint": "~6.1.0",
191199
"typescript": "~4.3.2"
192200
<% if (props.tools.includes('prettier')) { -%>
193201
},
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{
2+
"extends": [
3+
<% if (props.tools.includes('prettier')) { -%>
4+
"tslint:recommended",
5+
"tslint-config-prettier"
6+
<% } else { -%>
7+
"tslint:recommended"
8+
<% } -%>
9+
],
10+
"rulesDirectory": [
11+
"codelyzer"
12+
],
13+
"rules": {
14+
"array-type": false,
15+
"arrow-parens": false,
16+
"deprecation": {
17+
"severity": "warning"
18+
},
19+
"import-blacklist": [
20+
true,
21+
"rxjs/Rx"
22+
],
23+
"max-line-length": [
24+
true,
25+
120
26+
],
27+
"member-access": false,
28+
"member-ordering": [
29+
true,
30+
{
31+
"order": [
32+
"public-static-field",
33+
"protected-static-field",
34+
"private-static-field",
35+
"public-instance-field",
36+
"protected-instance-field",
37+
"private-instance-field",
38+
"public-static-method",
39+
"protected-static-method",
40+
"private-static-method",
41+
"constructor",
42+
"public-instance-method",
43+
"protected-instance-method",
44+
"private-instance-method"
45+
]
46+
}
47+
],
48+
"interface-name": false,
49+
"max-classes-per-file": false,
50+
"no-consecutive-blank-lines": false,
51+
"no-console": [
52+
true,
53+
"debug",
54+
"time",
55+
"timeEnd",
56+
"trace"
57+
],
58+
"no-duplicate-variable": [
59+
true,
60+
"check-parameters"
61+
],
62+
"no-empty": false,
63+
"no-inferrable-types": [
64+
true,
65+
"ignore-params"
66+
],
67+
"no-non-null-assertion": true,
68+
"no-redundant-jsdoc": true,
69+
"no-switch-case-fall-through": true,
70+
"no-unnecessary-initializer": true,
71+
"object-literal-sort-keys": false,
72+
"quotemark": [
73+
true,
74+
"single"
75+
],
76+
"typedef": [
77+
true,
78+
"parameter",
79+
"property-declaration"
80+
],
81+
"variable-name": false,
82+
"no-var-requires": false,
83+
"object-literal-key-quotes": false,
84+
"ordered-imports": false,
85+
"trailing-comma": false,
86+
"no-conflicting-lifecycle": true,
87+
"no-output-native": true,
88+
"directive-selector": [
89+
true,
90+
"attribute",
91+
"app",
92+
"camelCase"
93+
],
94+
"component-selector": [
95+
true,
96+
"element",
97+
"page",
98+
"app",
99+
"kebab-case"
100+
],
101+
"template-banana-in-box": true,
102+
"template-no-negated-async": true,
103+
"no-output-on-prefix": true,
104+
"no-inputs-metadata-property": true,
105+
"no-outputs-metadata-property": true,
106+
"no-host-metadata-property": true,
107+
"no-input-rename": true,
108+
"no-output-rename": true,
109+
"use-lifecycle-interface": true,
110+
"use-pipe-transform-interface": true,
111+
"component-class-suffix": true,
112+
"contextual-lifecycle": true,
113+
"directive-class-suffix": true
114+
}
115+
}

0 commit comments

Comments
 (0)