This project was generated using Nx.
🔎 Smart, Fast and Extensible Build System
10-minute video showing all Nx features
Nx supports many plugins which add capabilities for developing different types of applications and different tools.
These capabilities include generating applications, libraries, etc as well as the devtools to test, and build projects as well.
Below are our core plugins:
- Angular
ng add @nrwl/angular
- React
ng add @nrwl/react
- Web (no framework frontends)
ng add @nrwl/web
- Nest
ng add @nrwl/nest
- Express
ng add @nrwl/express
- Node
ng add @nrwl/node
There are also many community plugins you could add.
Run nx graph
to see a diagram of the dependencies of your projects.
Visit the Nx Documentation to learn more.
- Table of Contents
- Local Development Environment Setup
- Unit Testing
- Project Folder Structure
- Generators
- Naming Convention
- Source Code Repo
- Unit Testing
- Node.js v16 (or above)
- Angular CLI v14 (or above)
- Chrome
- Augury extention (recommended)
- Redux extention (recommended)
- VS Code v1.54 (or above)
- ESLint extention
- Prettier extention
- Angular Essentials extension (recommended)
- https://code.visualstudio.com/
- NX CLI
- Install the cli globally via
npm i -g @nrwl/cli
- https://www.npmjs.com/package/@nrwl/cli
- Install the cli globally via
- AWS CLI
- AWS SAM CLI
- Open a command line tool
- Create a local folder for the git repo
- Go to creatd folder
git clone <GIT_REPO_URL> .
git checkout dev
npm install
ng serve
The project is created using Angular CLI and the local DEV server will run at http://localhost:4200 by default.
- Open a command line tool
- Go to repo folder
npm test
- Open a command line tool
- Go to repo folder
npm run test:watch
- Open project in VS Code
- Go to Debug tab on the side nav
- Set breakpoints
- Press F5
apps
folder- Contains entry point apps and lambda functions
libs
folder- Contains NX libraries imported by the apps
dist
folder- Contains the build output of the apps
tools
folder- Contains workspace generators and other scripts
-
Client Library
- Contains components, containers, & state used to manage the client feature module
- Imported once in the pcff-app AppModule
-
Shared Library
- Contains shared code that will be used across your app and feature modules, e.g. global pipes or global UI component.
- Shared Module should be used mostly in Feature Modules
-
containers
folder - Container components- Subscribe to state changes from @ngxs store and provide data to Presentation Component
- Handle event from Presentation Component and dispatch actions to
@ngxs
store - The HTML template of Container Components is composed by Presentational Components with no HTML code or dependency to 3rd party UI components
-
components
folder - Presentation Component- Define UI
- Get data from Container Components with
@Input
- Emit event to Container Components with
@Output
-
actions
folder- Contains
@ngxs
action creators
- Contains
-
state
folder- Contains
@ngxs
state specific to the module
- Contains
-
models
folder- Contains models specific to the module
-
services
folder- Contains services specific to the module
- Services are mostly responsible for making HTTP calls to backend
- Services are consumed by the state
- Shared services should be moved to the
shared
library
The Nx workspace includes built-in and custom generators for our workspace. Generators are scripts that take inputs and scaffold our code with new components, services, state, etc.
To use a generator, you can invoke the generate
command in the Nx console. Choose from one of the built-in generators or scroll to the bottom and pick the custom workspace-generator
options.
The Nx workspace manages all applications and their dependencies. It comes with build-in generators to scaffold the correct files and to update the necessary files.
Depending on the project type (web, api, or library) you want to add will depend on which generator you use.
- Web applications should use the
@nrwl/angular - application
generator - Libraries should use the
@nrwl/angular - library
generator - AWS Lambdas should use the
@nrwl/node - application
or@nrwl/nest - application
generators - API libraries should use the
@nrwl/node - library
or@nrwl/nest - library
generators
Type | Convention | Class Name |
---|---|---|
Angular Module | .module.ts | *Module |
Angular Routing Module | <module-name> -routing.ts |
*RoutingModule |
Presentation Component | *.component.ts | *component |
Container Component | *-container.component.ts | *ContainerComponent |
Container Component (Modal) | *-modal.component.ts | *ModalComponent |
Directive | *.directive.ts | *Directive |
Pipe | *.pipe.ts | *Pipe |
Action/Action Creator | *.action.ts | N/A |
State | *.state .ts | *State |
Service | *.service.ts | *Service |
Unit Test | <target-file-name> .spec.ts |
N/A |
Type | Naming Standard | Sample |
---|---|---|
Variable | Camel case | fooVar: string; |
Function | Camel case | fooFunction() {} |
Constant variable | Camel case | const weeksInYear = 52; |
Exported constant variable | Upper case | export const DAYS_IN_YEAR = 365; |
Observable variable | Add "$" suffix to Observable variable | isPending$: Observable<boolean>; |
For example,
fooVar: string;
fooFunction() {}
const weeksInAYear = 52;
export const DAYS_IN_YEAR = 365;
export const logout = 'Logout';
isPending$: Observable<boolean> = this.store.select(HomeState.isLoginPending());
The only reasons to use a class is to check if a variable is of that instance using the instanceof
operator, or to create injectable Angular components/services. It's encouraged that you avoid classes unless necessary and instead export your functions. This will allow webpack to perform any tree-shaking to remove unused code. It's also a good practice for modular design patterns.
Type | Naming Standard | Sample |
---|---|---|
Class Name | Pascal case | class Foo {} |
Abstract Class Name | Prefix "Abstract" to class name | abstract class AbstractFoo {} |
Public class variable | Camel case | public fooVar: string; |
Private/Protected class variable | Add leading underscore to camel case | private _barVar: string; |
Public class function | Camel case | public fooFunction() {} |
Private/Protected class function | Add leading underscore to camel case | private _barFunction() {} |
For example,
class Foo {
fooVar: string;
private _barVar: string;
fooFunction() {}
protected _protectedFunction() {}
private _barFunction() {}
}
abstract class AbstractFoo {
// ...
}
Type | Naming Standard | Sample |
---|---|---|
Interface Name | Pascal case | interface Foo {} |
Interface variable | Camel case | fooVar: string; |
Interface function | Camel case | fooFunction() {} |
For example,
interface Foo {
fooVar: string;
barFunction();
}
Type | Naming Standard | Sample |
---|---|---|
Enum Name | Pascal case | enum Color {} |
Enum member | Pascal case | Red |
For example,
enum Color {
Red;
Blue;
Green;
}
- Use single quote (') unless escaping
For example,
let name = 'John Doe';
- Generic type must start with "T"
For example,
function identity<TPerson>(arg: TPerson): TPerson {
return arg;
}
We will adopt Development Isolation
branching model at the beginning.
- All team members will use the
dev
branch during sprint. - Code will be merged to the
master
branch for PROD release only.
As the team matures or the need to support more complex release planning arise, other branching models (e.g. feature isolation
, release isolation
) will be considered.
- Commit Related Changes
- Commit Often
- Don't commit half-done work
- Test code before commit
- Write good commit message
- Fix failed commit ASAP
- Use the imperative mood in the subject line. That is as if you were commanding someone. Start the line with “Fix”, “Add”, “Change” instead of “Fixed”, “Adding”, “Changes”.
- Limit the subject line to 50 characters
- Capitalize the subject line
- Separate subject from body with a blank line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
- Don’t end the summary line with a period
Short summary of changes (50 chars or less)
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded by a
single space, with blank lines in between, but conventions vary here
The target unit test coverage of this project is 80%
.
This section lists the major testing frameworks/libraries used by the project.
jest
is a testing framework created by Facebook, which continually gains steam. As compared with the out-of-box karma
+ jasmine
testing frameworks for Angular project, jest
comes with the following main benefits:
jest
is way faster thankarma
. One of the reasons for that is becausejest
doesn’t start a browser. Instead, it uses a virtual dom calledjsdom
.jest
contains improved test reports. The reports are much more readable and more comfortable to grasp.- Integrated coverage out of the box — no more need for
instanbul
. - Excellent command-line interface with a lot of advanced options. Run only one test, run tests according to a regex pattern or run only failed tests.
spectator
is a unit testing library designed for Angular to remove boilerplate codes for unit testing.
Here's a quick comparison.
-
Testing with Angular TestBed
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CommonBannerComponent } from './common-banner.component'; describe('CommonBannerComponent', () => { let component: CommonBannerComponent; let fixture: ComponentFixture<CommonBannerComponent>; beforeEach(async(() => { void TestBed.configureTestingModule({ declarations: [CommonBannerComponent], }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(CommonBannerComponent); component = fixture.componentInstance; }); it('should compile', () => { expect(component).toBeTruthy(); }); });
-
Testing with Spectator
import { Spectator, createComponentFactory } from '@ngneat/spectator/jest'; import { CommonBannerComponent } from './common-banner.component'; describe('CommonBannerComponent', () => { let spectator: Spectator<CommonBannerComponent>; const createComponent = createComponentFactory({ component: CommonBannerComponent, }); beforeEach(() => (spectator = createComponent())); it('should compile', () => { expect(spectator.component).toBeTruthy(); }); });
ng-mocks
is used to mock components, modules, directives, pipes, services, etc. in Angular.
For mocking regular functions, use
jest.fn()
.
When mocking dependent modules, one exception is the
RouterModule
, whereRouterTestingModule
provides better support for unit testing.
For example,
// imports ...
describe('TranscriptOrderDetailComponent', () => {
let spectator: Spectator<TranscriptOrderDetailComponent>;
const createComponent = createComponentFactory({
component: TranscriptOrderDetailComponent,
declarations: [
MockComponent(TranscriptOrderDetailHeaderComponent),
MockComponent(OrderDetailsComponent),
MockComponent(HearingDetailsComponent),
MockComponent(TranscriptDetailsComponent),
MockComponent(MessagesComponent),
MockComponent(MessageTmsComponent),
],
imports: [
RouterTestingModule,
MockModule(FlexLayoutModule),
MockModule(AngularComponentsModule),
],
mocks: [Router],
});
beforeEach(() => (spectator = createComponent()));
it('should compile', () => {
expect(spectator.component).toBeTruthy();
});
// ...
}
jest-marbles
is a set of helper functions and Jest matchers for RxJs marble testing.
-
imports
import {cold, hot, time} from 'jest-marbles';
-
matchers
expect(stream).toBeObservable(expected); expect(stream).toBeMarble(marbleString); expect(stream).toHaveSubscriptions(marbleString); expect(stream).toHaveSubscriptions(marbleStringsArray); expect(stream).toHaveNoSubscriptions(); expect(stream).toSatisfyOnFlush(() => { expect(someMock).toHaveBeenCalled(); })