- 1. ng-jest-cypress-template
- Extras
- App Actions
https://docs.cypress.io/guides/component-testing/angular/examples
https://angularexperts.io/blog/top-10-angular-architecture-mistakes
Even a one-pager (single feature) application without navigation, this first page / feature should be implemented as the first lazy loaded feature!
I would personally recommend to always define lazy route with the routes based feature with loadChildren which is the most modern and flexible way of doing things. Then, in case our lazy feature contains sub navigation, we can lazy load additional components with loadComponent.
ng generate component --name features/home --inline-style --inline-template
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
pathMatch: 'full',
redirectTo: 'home',
},
// application with a single feature
// implemented as a first lazy loaded feature
{
path: 'home',
loadChildren: () =>
import('./features/home/home.routes').then((m) => m.routes),
},
];
// home.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./home.component').then((m) => m.HomeComponent),
},
// which is easy to extend in the future, eg
{
path: 'editor',
loadComponent: () =>
import('./home-editor.component').then((m) => m.HomeEditorComponent),
},
// or a larger sub-feature
{
path: 'forecast',
loadChildren: () =>
import('./forecast/forecast.routes').then((m) => m.routes),
},
];
https://github.com/pahen/madge
npx madge src/main.ts --ts-config tsconfig.json --image ./deps.webp
https://github.com/javierbrea/eslint-plugin-boundaries
ESLINT_PLUGIN_BOUNDARIES_DEBUG=1 npm run lint
firebase target:apply hosting main first-try-262cf
firebase target:apply hosting other my-site-e7a1
firebase deploy --only hosting:main
firebase hosting:channel:deploy preview --only main
This project was generated with Angular CLI version 12.2.8.
ng new ng-jest-cypress-template --style=scss --routing --strict
https://github.com/angular-eslint/angular-eslint
ng add @angular-eslint/schematics
npm install --save-dev --save-exact prettier
package.json
package-lock.json
dist
build
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"endOfLine": "auto"
}
npm install --save-dev --save-exact eslint-config-prettier
{
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"prettier"
]
}
{
"scripts": {
"prettier:check": "prettier --check .",
"prettier:write": "prettier --write ."
}
}
https://github.com/thymikee/jest-preset-angular
npm remove karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter @types/jasmine jasmine-core
rm ./karma.conf.js ./src/test.ts
npm install --save-dev --save-exact jest jest-preset-angular @types/jest
In project root:
import 'jest-preset-angular/setup-jest';
In project root:
require('jest-preset-angular/ngcc-jest-processor');
module.exports = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
testPathIgnorePatterns: ['<rootDir>/cypress/'],
};
See https://github.com/cypress-io/cypress-and-jest-typescript-example
{
// Required for jest to work with cypress
"include": ["src/*.ts"],
"exclude": ["src/*.spec.ts"]
//
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"outDir": "./out-tsc/spec",
"types": ["jest"]
},
"files": ["src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}
{
"scripts": {
// ...
"test": "jest"
// ...
}
}
npm install --save-dev --save-exact cypress
npx cypress open
{
"baseUrl": "http://localhost:4200",
"defaultCommandTimeout": 10000
}
{
"scripts": {
"cypress:open": "cypress open",
"cypress:run": "cypress run"
}
}
In cypress
folder
{
"extends": "../tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"strict": true,
"types": ["cypress"]
},
"include": ["**/*.ts"]
}
In cypress/integration
folder.
it('works', () => {
cy.wrap('foo').should('equal', 'foo');
});
In cypress\support
folder.
// we could place this url into cypress.json as "baseUrl"
const url = 'http://localhost:4200';
export const navigateTo = () => cy.visit(url);
export const getGreeting = () => cy.get('.toolbar > span');
In cypress\integrations\
import { navigateTo, getGreeting } from '../support/po';
describe('Hello Angular', () => {
beforeEach(navigateTo);
it('should display welcome message', () => {
getGreeting().contains('Welcome');
});
});
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@app/*": ["src/app/*"]
}
}
}
module.exports = {
/*
https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping/
tsconfig.json
"paths": {
"@app/*": ["src/app/*"]
}
*/
moduleNameMapper: {
'^@app/(.*)$': '<rootDir>/src/app/$1',
},
};
- https://glebbahmutov.com/blog/testing-angular-application-via-app-actions/
- https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/
- https://github.com/import-js/eslint-plugin-import
- https://github.com/jest-community/eslint-plugin-jest
- https://github.com/testing-library/eslint-plugin-jest-dom
- https://github.com/testing-library/eslint-plugin-testing-library
- https://github.com/testing-library/angular-testing-library/blob/main/apps/example-app/src/app/examples/02-input-output.spec.ts
- https://github.com/testing-library/jest-dom
- https://github.com/testing-library/user-event
https://docs.cypress.io/guides/component-testing/component-test-troubleshooting