OctoPer’s load test IDE (Kraken) is an Angular project with multiple front-end applications,

  • Admin UI for managing Docker containers and container images
  • Both the Gatling UIs are based on Angular8 and share many components, CSS, and external library dependencies.

This post is intended as a reference guide for every Developer who wants to create an Angular Workspace with multiple applications and libraries.

Based on my experience in creating Kraken, first explain how to use Angular/Cli to generate such a project, then share resources and unit tests, and give tips on deployment

Angular multi-application project creation

First, let’s know what An Angular Workspace is. A Workspace is a set of Angular applications and libraries. Angular. json is the root level file of the Angular Workspace that provides Workspace scopes and project-specific (application or library) configuration defaults for build and development tools.

You want to be able to use the Angular CLI to generate an Angular workspace and its configuration. That’s the topic of this article: how to use the Angular Cli to generate workspaces and their applications and libraries.

Remember that projects can be either applications or libraries in Angular terms. Both are stored in the projects workspace folder and configured in the root angular.json file.

A prerequisite for

Angular requires Node.js version 10.9.0 or later (run Node -v to view the current version). Angular CLI-generated Angular applications rely on external libraries in the form of NPM packages. Node.js includes an NPM package manager. Run NPM -v to check the NPM version. I recommend using the command to update NPM to the latest available version (6.10.0 as of this blog post) NPM install -g npm@latest.

Finally, you need the Angular Cli to install it globally with the following command:

    npm install -g @angular/cli
Copy the code

Create a Workspace

Ng new

command user to generate workspace

  ng new kraken --createApplication=false --directory=frontend --interactive=false
Copy the code
  • --createApplication=falseParameter to avoid creating an initial application (default is true). Otherwise, Angular CLI SRC creates an application in a folder in the new workspace. Build the application in a subfolder (Projects) in the workspace.
  • --interactive=falseParameters are used here to avoid prompting the Angular CLI to create a project with useless parameters, such as whether the initial application (which we don’t generate) should contain a routing module or a CSS preprocessor to use.
  • --directory=frontendThe parameter is the name of the directory to initialize the workspace. Default is the workspace name.

As we can see in the screenshot above, this command generates several files in the Frontend folder

The first is the Angular Workspace configuration file angular.json. Currently, it contains only information about the location of projects (applications and libraries). Once we generate something, it gets really complicated.

    {
      "$schema": "./node_modules/@angular/cli/lib/config/schema.json"."version": 1."newProjectRoot": "projects"."projects": {}}Copy the code

The package.json file lists all dependencies Angular needs. Since the ng new command also installs NPM dependencies, the package-Lock. json file is generated along with the node_modules folder containing the downloaded dependencies.

The tsconfig.json file specifies typescript compiler options (TS is used by the Anuglar project). The tslint.json file configuration typescript short lint is a tool for analyzing source code and flagting programming errors, errors, stylistic errors, and suspicious constructs.

Finally, readme.md also generates a file. Read this document for information about Angular CLI usage: How to build, service, and test newly generated applications

Create an application

The ng generate application

command is used by projects to create a new application in a subfolder in the workspace. Run the following command to generate two applications: Administration and Gatling:

    cd frontend
    ng generate application administration --style=scss --routing=true
    ng generate application gatling --style=scss --routing=true
Copy the code
  • Parameter –style= SCSS Sets the SCSS preprocessor for the style file (default: CSS) — the parameter can be left out.
  • The –routing=true argument tells the Angular CLI to generate a routing NgModule without it.

These commands generate administration and Gatling projects in the subfolder projects, which contain the following files (and regular Angular CLI-generated projects, missing package.json files)

  • Two tsconfig.*.ts files. They extend the root of the workspace, tsconfig.ts, and set specific configurations to compile the application (tsconfig.app.ts) or its unit tests (tsconfig.spec.ts). Read more about TypeScript configuration in the Angular documentation.
  • This SRC folder contains all TypeScript, HTML, and CSS sources for your application (more on SRC folder structure).
  • Karma. Conf.js Karma configuration file for unit tests.
  • E2e A folder for running end-to-end tests with protractors.

The command also updates the root angular.json file to add two application configurations:

{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "administration": { "projectType": "application", [...]  }, "gatling": { "projectType": "application", [...]  } }, "defaultProject": "administration" }Copy the code

We’ll look at the configuration for each application project later, because we’ll have to update them to share resources.

Create a library (librany)

The ng generate library

command is used to generate a library. In the workspace folder (frontend if you follow the instructions in this tutorial), run the following command:

    ng generate library tools
    ng generate library vendors

Copy the code

Here again, angular.json updates files with two newly created libraries:

{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "administration": { [...]  }, "gatling": { [...]  }, "tools": { "projectType": "library", [...]  } }, "vendors": { "projectType": "library", [...]  } }, "defaultProject": "administration" }Copy the code

It also creates files for each library, projects/ Tools and projects/vendors. The file is similar to the file when the application is generated.

One thing to note is that you cannot specify a style preprocessor when you build the library. There is no configuration related to this in the workspace configuration (file) of the Angular. json library. You must specify a style each time you generate a component in the library, for example using Command ng generate component my-component –style= SCSS –project=tools.

Creating a Shared Service

The idea here is to generate the service in the library and use it in the application. Let’s create a virtual service in the Tools library:

    ng generate service hello-world --project=tools

Copy the code

The syntax is ng generate service

and the parameter –project is required to specify which library to generate the service in.

This will create a file called projects/tools/ SRC /hello-world.service.ts and its unit tests (projects/tools/ SRC /hello-world.service.spec.ts). Update it to create a simple getter:

    import {Injectable} from '@angular/core';

    @Injectable({
      providedIn: 'root'
    })
    export class HelloWorldService {
      get message(): string {
        return 'Hello World! '; }}Copy the code
Note: This service is declared injectable with the providedIn: 'root' option. This means that the service is a singleton. It can be used in any component or service without having to provide it. No matter where you use it, it's always the same instance. Without this option, we might inject an instance @Component({providers: [HelloWorldService]}) for each instance using the per-instance syntax. We can also use syntax to do the same thing at the module level @ngModule ({providers: [HelloWorldService]}). Read more about dependency injection in the Angular documentation.Copy the code

Update AppComponent projects/administration/src/app/app.com ponent. Ts with the above services

    import {Component} from '@angular/core';
    import {HelloWorldService} from 'projects/tools/src/lib/hello-world.service';

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']})export class AppComponent {
      title = 'administration';

      constructor(helloWorld: HelloWorldService) {
        this.title = helloWorld.message; }}Copy the code

Student: If you use thetaIntelliJIdeaorWebstorm,HelloWorldThe default import of the service isimport {HelloWorldService} from '.. /.. /.. /tools/src/lib/hello-world.service';. If you move files around, this can be ugly and difficult to maintain. You can open project Settings and choose TypeScript> Import > Use path relative to tsconfig.json:

The installationAngular Material UI

Angular Material is a set of components that follow the conventions of Material Design Settings. With the Angular CLI, you can install Angular Material on both applications by simply running the following command.

    ng add @angular/material
    ng add @angular/material --project=gatling
Copy the code
Note: the first command does not require the --project=administration parameter because it is the defaultProject in the workspace configuration ("defaultProject": "administration" in the angular.json file).Copy the code

These two commands add a dependency @angular/material to the package.json file and also automatically update various project files to add appropriate styles and import fonts and NgModules:

    UPDATE projects/administration/src/main.ts (391 bytes)
    UPDATE projects/administration/src/app/app.module.ts (502 bytes)
    UPDATE angular.json (10132 bytes)
    UPDATE projects/administration/src/index.html (482 bytes)
    UPDATE projects/administration/src/styles.scss (181 bytes)
Copy the code

I found it much cleaner to regroup external dependencies into a single NgModule. That’s why we created a vendors library earlier.

So, let’s update VendorsModule file projects/vendors/SRC/lib/vendors. The module. The ts to import and export MatButtonModule, thus in one place rearrange our external dependencies:

    import {NgModule} from '@angular/core';
    import {MatButtonModule} from '@angular/material';

    @NgModule({
      imports: [MatButtonModule],
      exports: [MatButtonModule]
    })
    export class VendorsModule { }
Copy the code

Then, we import in the AppModule VendorsModule projects/administration/SRC/app/app. The module. The ts:

    @NgModule({[...].  imports: [ VendorsModule, ], [...] })export class AppModule { }
Copy the code

Finally, using the instructions of the mat – raised – buttonAppComponent projects/administration/src/app/app.com ponent. HTML:

    <div style="text-align:center">
      <h1>
        Welcome to {{ title }}!
      </h1>
      <div><img width="300" alt="Angular Logo" src="data:image/svg+xml; base64,..."></div>
      <button mat-raised-button color="primary" >Click Me!</button>
    </div>
Copy the code

Start the application

To serve an Angular application, run the command ng serve –project=administration.

You can then open the web page at http://localhost:4200/ :

If you follow each step in this tutorial, you should see “Welcome to Hello World!” Message and presented material “Click Me! Button.

Otherwise, you can download the source code (only NPM Install runs before starting the application).

If you want to see a more complex application, go to the “Complete Application Example” chapter.

Also notice that if you want to start both the Administration and Gatling applications, you see error messages:

    Port 4200 is already in use. Use '--port' to specify a different port.
    Error: Port 4200 is already in use. Use '--port' to specify a different port.
Copy the code

Use the –port option for details please refer to the official documentation or read the chapter “How to Use HAProxy to Serve Multi-angle applications (described below)” to learn how to set the default port used by your application.

Angular shares resources with multiple applications

Let’s go back to the load test IDE. Splitting a large application like Kraken into several modules and libraries is a good idea. It simplifies maintenance and improves code reusability.

In terms of reusability, sharing CSS styles and assets across component libraries, it might be cleaner to have completely isolated modules, but since all my libraries and applications rely on Angular Material, I don’t want to import code repeatedly for every project.

Libraries and applications share CSS

In the introduction above, we imported one of the default Angular Material themes. To give Kraken the look and feel of an IDE, I want to use a custom Angular Material theme with the following theme:

  • Dark background
  • The color is better than the basic main color and other warning colors (add warning, success, danger and other colors),
  • Components are more densely laid out (there is no layer density yet). KrakenThe user interface of

To create this UI, I had to create several component libraries:

  • Adjustable split panel,
  • Console and file tree tabs,
  • Code editor,
  • And a master workspace that combines all of this.

They all use generic CSS located in the styles folder at the root of the workspace. For example, the custom Material theme SCSS file initializes Material Design and declares custom colors:

    @import '.. /node_modules/@angular/material/theming';

    [...].

    $app-primary: mat-palette($mat-blue);
    $app-accent: mat-palette($mat-green);
    $app-info: mat-palette($mat-light-blue);
    $app-success: mat-palette($mat-light-green);
    $app-error: mat-palette($mat-deep-orange);
    $app-background: mat-palette($mat-gray);

    [...].

    $mat-theme: (
      [...]
    );

    @include angular-material-theme($mat-theme);

Copy the code

And a compact Scss file that declares custom CSS classes for changing component density:

    @import 'app-padding';
    @import 'app-font';

    // Must also disable the ripple effect
    @mixin compact-checkbox($size: 34px) {
      $half: $size / 2;
      .mat-checkbox-inner-container {
        height: $half;
        width: $half; }} @mixin compact-button($size: 34px) {
      .mat-icon-button {
        width: $size;
        height: $size;
        line-height: $size * 0.9;

        .ng-fa-icon {
          font-size: $size * 0.5; }}}[...].

Copy the code

To use these SCSS files in management and Gatling load testing applications, we must declare them in Angular workspace configuration angular.json:

    "stylePreprocessorOptions": {
      "includePaths": [
        "styles"
      ]
    },
Copy the code

In stylePreprocessorOptions you must join Projects > Administration/Gatling > Architect > Build > Options.

When running unit tests, the following error may occur if some of the components under test require a common style:

ERROR in .. /icon/src/lib/icon/icon.component.scss Module build failed (from /home/kojiro/workspaces/kraken/frontend/node_modules/sass-loader/lib/loader.js): @import 'app-margin'; ^ Can't find stylesheet to import.Copy the code

In this case, simply add stylePreprocessorOptions to the Projects > My-Project > Architect > Test > Options workspace configuration section.

Sharing assets across libraries and applications

In addition to common CSS files, you may want to share assets between applications and libraries.

For example, in Kraken’s assets, we store:

  • Kraken logo,
  • Javascript files used by Ace editor. In fact, due to configuration, the Ace theme and schema files are in the Ext folder and have been copied angular.json. For example, the Gatling Build Architect option includes the following configuration:
    "assets": [
      "projects/gatling/src/favicon.ico"."projects/gatling/src/assets",
      {
        "glob": "* * / *"."input": "assets/"."output": "assets/"
      },
      {
        "glob": "**/worker-*.js"."input": "node_modules/ace-builds/src-min/"."output": "assets/ace/"},].Copy the code

It begins by declaring assets specific to the Gatling application. It then uses the syntax “glob”: “**/*” to include all the files in the root assets folder. Finally, all Ace methods are copied into assets/ Ace /.

We also use a number of other javascript files associated with the Ace code editor. These themes, patterns, and code segments must be declared build in the Scripts section of the Architect option:

    "scripts": [
    "node_modules/ace-builds/src-min/mode-xml.js",
    "node_modules/ace-builds/src-min/mode-yaml.js",
    "node_modules/ace-builds/src-min/ext-searchbox.js",
    "node_modules/ace-builds/src-min/ext-language_tools.js",
    "node_modules/ace-builds/src-min/ext-modelist.js",
    "ext/mode-log.js",
    "ext/theme-kraken.js",
    "ext/snippets/scala.js"
  ]
Copy the code

Whatever you want to accomplish with assets and external scripts, you are advised to take a look at the Angular-CLI documentation page: Stories Asset Configuration.

Angular workspaces and unit tests

The Angular CLI allows you to run unit tests on specific projects using the following syntax:

    ng test --project=<my-project>
Copy the code
  • By default, it will run unit tests continuously to let you know if they succeeded or failed. If you are running this particular test on a unit test definition using FIT rather than IT (you can also use FDESCRIBE on a test suite).

  • It automatically generates test coverage reports in folders with options. /coverage/my-project–watch=false –codeCoverage=true

I want a report that recombines all coverage information and speeds up test execution by running unit tests in parallel. Therefore, I created the following script:

    #! /bin/bash
    rm -rf coverage
    rm -rf coverage-all

    for dir in projects/*; do
      if [["$dir"! = *-e2e]]
      then
        prefix="projects/";
        project=${dir#$prefix}; #Remove prefix
        echo "$project"
        ng test --watch=false --codeCoverage=true --sourceMap=true --project=$project &
      fi
    done

    wait  # Wait for all tasks to complete

    ./node_modules/istanbul/lib/cli.js report --dir ./coverage-all/ html

    google-chrome ./coverage-all/index.html &

Copy the code

It runs unit tests in parallel, with parallel coverage, for each project that does not end at the end – E2E (these are all end-to-end test projects). Then, use the Istanbul Client to assemble all the reports. Warning: If you have multiple projects, this script will run multiple Web browsers at the same time. It may freeze your computer while it is running!

Istanbul needs a JSON report to put it together. Therefore, update the karmap.conf.js file as well. They are located at the root of each application or library. Simply add the JSON report:

coverageIstanbulReporter: { dir: require('path').join(__dirname, '.. /.. /coverage/analysis'), reports: ['html', 'lcovonly', 'json'], fixWebpackSourcePaths: true },Copy the code

If you want to know how to write unit tests, check out the Angular documentation for tests.

How to use the HAProxy service to serve multiple Angular applications

In Kraken, we have two Angular applications: Administration and Gatling.

The idea is to serve on different ports and base Href; They are then packaged as docker images that are used on the same port but with a different base Href. Package them as docker images for use on the same port, but with different base Href.

In particular ports andbase HrefDeploy the Angular application on

By default, Angular serves all applications at the same URL and port: http://localhost: 4200.

To be able to launch both applications during development, change the port of one of them in the Angular Workspace configuration Angular. json file. For example, in Kraken, the Serve architect for the Gatling application is configured to service it on port 4222:

    "serve": {
      "builder": "@angular-devkit/build-angular:dev-server",
      "options": {
        "browserTarget": "gatling:build",
        "port": 4222
      },
      "configurations": {
        "production": {
          "browserTarget": "gatling:build:production"
        }
      }
    },
Copy the code

Ng serve then runs the command with the following parameters:

    ng serve --open --project=gatling --baseHref /gatling/
Copy the code

Due to the parameter and configuration, it will open Web browser via the URL http://localhost:4222/gatling.

Package Angular apps as Docker images

Angular applications can be built with the angular-cli ng build command:

    ng build gatling --prod --baseHref /gatling/
Copy the code

Again, here –baseHref /gatling/ is used to specify the baseHref. The application is built into the folder Dist/Gatling. The following DOCKERFILE generates an NGINX Docker image containing the built application:

FROM nginx:1.15.9-alpine ARG APPLICATION COPY nginx.conf /etc/nginx.conf WORKDIR /usr/share/nginx.html COPY dist/$APPLICATION .
    RUN ls -laR .
    EXPOSE 80
Copy the code

The output of the ng build command is copied to a folder of /usr/share/nginx/html images using the following nginx configuration:

worker_processes 1; events { worker_connections 1024; } http { server { listen 80 default_server; server_name localhost; root /usr/share/nginx/html; index index.html index.htm; include /etc/nginx/mime.types; gzip on; gzip_min_length 1000; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml  application/xml+rss text/javascript; location / { try_files $uri $uri/ /index.html; }}}Copy the code

Run the following command to generate the Docker image:

    docker build --rm=true -t gatling-ui:latest --build-arg APPLICATION=gatling .
Copy the code

Use HAProxy to provide production mirroring

HAProxy is open source software that provides a highly available proxy server for TCP – and HTTP-based applications. For Kraken, we use it to redirect HTTP requests on the appropriate Frontend, depending on the base Href (thanks to the path_beg/administration keyword). Here is the configuration file:


    global

    defaults
        mode    http
        option  forwardfor
        option  http-server-close
        # Set the max time to wait for a connection attempt to a server to succeed
        timeout connect 30s
        # Set the max allowed time to wait for a complete HTTP request
        timeout client  50s
        # Set the maximum inactivity time on the server side
        timeout server  50s
        # handle the situation where a client suddenly disappears from the net
        timeout client-fin 30s

    frontend http-in
        bind *:80
        mode http

        acl has_administration  path_beg /administration
        acl has_gatling  path_beg /gatling

        use_backend kraken-administration if has_administration
        use_backend kraken-gatling ifhas_gatling backend kraken-administration server kraken-administration-ui kraken-administration-ui:80 check fall 3 rise 2 reqrep ^([^\ ]*\ /)administration[/]? (.*) \1\2 backend kraken-gatling server kraken-gatling-ui kraken-gatling-ui:80 check fall 3 rise 2 reqrep ^([^\ ]*\ /)gatling[/]? 1 \ \ 2 (. *)Copy the code

The easiest way to start it is to use the HAProxy Docker image.

To see the full example (using starting two front-end applications, multiple back-end servers, and HAProxy Docker-compose).

Complete application

I hope this blog post has been helpful to anyone who wants to try out the complexities of Angular development in a multi-project workspace.

The source code for Kraken’s Angular front-end is available on GitHub. Check out the full, up-to-date code sample. If you think I missed an important part of Angular development or made a mistake, please leave me a comment.

Finally, if you want to learn more about Kraken for the load testing IDE, check out its GitHub page.

Blog address: address

Original address:

The original address