summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/ng-schematics/tools/projects.mjs
blob: 1c6e346a6a72d361443c0c2abb5d2847b077403a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
 * @license
 * Copyright 2023 Google Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

import {spawn} from 'child_process';
import {randomUUID} from 'crypto';
import {readFile, writeFile} from 'fs/promises';
import {join} from 'path';
import {cwd} from 'process';

class AngularProject {
  static ports = new Set();
  static randomPort() {
    /**
     * Some ports are restricted by Chromium and will fail to connect
     * to prevent we start after the
     *
     * https://source.chromium.org/chromium/chromium/src/+/main:net/base/port_util.cc;l=107?q=kRestrictedPorts&ss=chromium
     */
    const min = 10101;
    const max = 20202;
    return Math.floor(Math.random() * (max - min + 1) + min);
  }
  static port() {
    const port = AngularProject.randomPort();
    if (AngularProject.ports.has(port)) {
      return AngularProject.port();
    }
    return port;
  }

  static #scripts = testRunner => {
    return {
      // Builds the ng-schematics before running them
      'build:schematics': 'npm run --prefix ../../ build',
      // Deletes all files created by Puppeteer Ng-Schematics to avoid errors
      'delete:file':
        'rm -f .puppeteerrc.cjs && rm -f tsconfig.e2e.json && rm -R -f e2e/',
      // Runs the Puppeteer Ng-Schematics against the sandbox
      schematics: 'schematics ../../:ng-add --dry-run=false',
      'schematics:e2e': 'schematics ../../:e2e --dry-run=false',
      'schematics:config': 'schematics ../../:config --dry-run=false',
      'schematics:smoke': `schematics ../../:ng-add --dry-run=false --test-runner="${testRunner}" && ng e2e`,
    };
  };
  /** Folder name */
  #name;
  /** E2E test runner to use */
  #runner;

  constructor(runner, name) {
    this.#runner = runner ?? 'node';
    this.#name = name ?? randomUUID();
  }

  get runner() {
    return this.#runner;
  }

  get name() {
    return this.#name;
  }

  async executeCommand(command, options) {
    const [executable, ...args] = command.split(' ');
    await new Promise((resolve, reject) => {
      const createProcess = spawn(executable, args, {
        shell: true,
        ...options,
      });

      createProcess.stdout.on('data', data => {
        data = data
          .toString()
          // Replace new lines with a prefix including the test runner
          .replace(/(?:\r\n?|\n)(?=.*[\r\n])/g, `\n${this.#runner} - `);
        console.log(`${this.#runner} - ${data}`);
      });

      createProcess.on('error', message => {
        console.error(`Running ${command} exited with error:`, message);
        reject(message);
      });

      createProcess.on('exit', code => {
        if (code === 0) {
          resolve(true);
        } else {
          reject();
        }
      });
    });
  }

  async create() {
    await this.createProject();
    await this.updatePackageJson();
  }

  async updatePackageJson() {
    const packageJsonFile = join(cwd(), `/sandbox/${this.#name}/package.json`);
    const packageJson = JSON.parse(await readFile(packageJsonFile));
    packageJson['scripts'] = {
      ...packageJson['scripts'],
      ...AngularProject.#scripts(this.#runner),
    };
    await writeFile(packageJsonFile, JSON.stringify(packageJson, null, 2));
  }

  get commandOptions() {
    return {
      ...process.env,
      cwd: join(cwd(), `/sandbox/${this.#name}/`),
    };
  }

  async runNpmScripts(command) {
    await this.executeCommand(`npm run ${command}`, this.commandOptions);
  }

  async runSchematics() {
    await this.runNpmScripts('schematics');
  }

  async runSchematicsE2E() {
    await this.runNpmScripts('schematics:e2e');
  }

  async runSchematicsConfig() {
    await this.runNpmScripts('schematics:config');
  }

  async runSmoke() {
    await this.runNpmScripts(
      `schematics:smoke -- --port=${AngularProject.port()}`
    );
  }
}

export class AngularProjectSingle extends AngularProject {
  async createProject() {
    await this.executeCommand(
      `ng new ${this.name} --directory=sandbox/${this.name} --defaults --skip-git`
    );
  }
}

export class AngularProjectMulti extends AngularProject {
  async createProject() {
    await this.executeCommand(
      `ng new ${this.name} --create-application=false --directory=sandbox/${this.name} --defaults --skip-git`
    );

    await this.executeCommand(
      `ng generate application core --style=css --routing=true`,
      this.commandOptions
    );
    await this.executeCommand(
      `ng generate application admin --style=css --routing=false`,
      this.commandOptions
    );
  }
}