projen 0.103.26 → 0.103.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/.jsii +714 -10
  2. package/lib/ai-instructions.js +2 -2
  3. package/lib/awscdk/auto-discover.js +6 -6
  4. package/lib/awscdk/awscdk-app-java.js +1 -1
  5. package/lib/awscdk/awscdk-app-py.js +1 -1
  6. package/lib/awscdk/awscdk-app-ts.js +1 -1
  7. package/lib/awscdk/awscdk-construct.js +1 -1
  8. package/lib/awscdk/awscdk-deps-java.js +1 -1
  9. package/lib/awscdk/awscdk-deps-js.js +1 -1
  10. package/lib/awscdk/awscdk-deps-py.js +1 -1
  11. package/lib/awscdk/awscdk-deps.js +1 -1
  12. package/lib/awscdk/cdk-config.js +3 -3
  13. package/lib/awscdk/cdk-tasks.js +1 -1
  14. package/lib/awscdk/integration-test.js +1 -1
  15. package/lib/awscdk/lambda-extension.js +1 -1
  16. package/lib/awscdk/lambda-function.js +2 -2
  17. package/lib/build/build-workflow.js +1 -1
  18. package/lib/cdk/auto-discover-base.js +2 -2
  19. package/lib/cdk/construct-lib.js +1 -1
  20. package/lib/cdk/integration-test-base.js +1 -1
  21. package/lib/cdk/jsii-build.js +1 -1
  22. package/lib/cdk/jsii-docgen.js +1 -1
  23. package/lib/cdk/jsii-project.js +1 -1
  24. package/lib/cdk8s/auto-discover.js +2 -2
  25. package/lib/cdk8s/cdk8s-app-py.js +1 -1
  26. package/lib/cdk8s/cdk8s-app-ts.js +1 -1
  27. package/lib/cdk8s/cdk8s-construct.js +1 -1
  28. package/lib/cdk8s/cdk8s-deps-py.js +1 -1
  29. package/lib/cdk8s/cdk8s-deps.js +1 -1
  30. package/lib/cdk8s/integration-test.js +1 -1
  31. package/lib/cdktf/cdktf-construct.js +1 -1
  32. package/lib/cdktn/cdktn-app-ts.js +1 -1
  33. package/lib/cdktn/cdktn-config.js +1 -1
  34. package/lib/cdktn/cdktn-construct.js +1 -1
  35. package/lib/cdktn/cdktn-deps.js +1 -1
  36. package/lib/cdktn/cdktn-tasks.js +1 -1
  37. package/lib/circleci/circleci.js +1 -1
  38. package/lib/component.js +2 -2
  39. package/lib/dependencies.js +1 -1
  40. package/lib/dev-env.js +1 -1
  41. package/lib/docker-compose/docker-compose-service.js +1 -1
  42. package/lib/docker-compose/docker-compose.js +1 -1
  43. package/lib/file.js +1 -1
  44. package/lib/gitattributes.js +1 -1
  45. package/lib/github/actions-provider.js +1 -1
  46. package/lib/github/actions.const.js +1 -1
  47. package/lib/github/auto-approve.js +1 -1
  48. package/lib/github/auto-merge.js +1 -1
  49. package/lib/github/auto-queue.js +1 -1
  50. package/lib/github/dependabot.js +1 -1
  51. package/lib/github/dependency-review.js +1 -1
  52. package/lib/github/github-credentials.js +1 -1
  53. package/lib/github/github-project.js +1 -1
  54. package/lib/github/github.js +1 -1
  55. package/lib/github/merge-queue.js +1 -1
  56. package/lib/github/mergify.js +1 -1
  57. package/lib/github/pr-template.js +1 -1
  58. package/lib/github/pull-request-backport.js +1 -1
  59. package/lib/github/pull-request-lint.js +1 -1
  60. package/lib/github/stale.js +1 -1
  61. package/lib/github/task-workflow-job.js +1 -1
  62. package/lib/github/task-workflow.js +1 -1
  63. package/lib/github/workflow-actions.js +1 -1
  64. package/lib/github/workflow-jobs.js +1 -1
  65. package/lib/github/workflow-steps.js +1 -1
  66. package/lib/github/workflows.js +1 -1
  67. package/lib/gitlab/configuration.js +1 -1
  68. package/lib/gitlab/gitlab-configuration.js +1 -1
  69. package/lib/gitlab/nested-configuration.js +1 -1
  70. package/lib/gitpod.js +1 -1
  71. package/lib/ignore-file.js +1 -1
  72. package/lib/ini.js +1 -1
  73. package/lib/java/java-project.js +1 -1
  74. package/lib/java/junit.js +1 -1
  75. package/lib/java/maven-compile.js +1 -1
  76. package/lib/java/maven-packaging.js +1 -1
  77. package/lib/java/maven-sample.js +1 -1
  78. package/lib/java/pom.js +2 -2
  79. package/lib/java/projenrc.js +1 -1
  80. package/lib/javascript/biome/biome.js +1 -1
  81. package/lib/javascript/bundler.js +1 -1
  82. package/lib/javascript/eslint.js +1 -1
  83. package/lib/javascript/index.d.ts +1 -0
  84. package/lib/javascript/index.js +2 -1
  85. package/lib/javascript/jest.js +4 -4
  86. package/lib/javascript/license-checker.js +1 -1
  87. package/lib/javascript/node-config-file.js +18 -4
  88. package/lib/javascript/node-native-test-runner.d.ts +234 -0
  89. package/lib/javascript/node-native-test-runner.js +368 -0
  90. package/lib/javascript/node-package.js +1 -1
  91. package/lib/javascript/node-project.js +1 -1
  92. package/lib/javascript/npm-config.js +1 -1
  93. package/lib/javascript/pnpm-workspace-config.js +2 -2
  94. package/lib/javascript/pnpm-workspace.js +1 -1
  95. package/lib/javascript/prettier.js +1 -1
  96. package/lib/javascript/projenrc.js +1 -1
  97. package/lib/javascript/typescript-config.js +2 -2
  98. package/lib/javascript/upgrade-dependencies.js +2 -2
  99. package/lib/javascript/yarnrc.js +1 -1
  100. package/lib/json-patch.js +1 -1
  101. package/lib/json.js +1 -1
  102. package/lib/license.js +1 -1
  103. package/lib/logger.js +1 -1
  104. package/lib/makefile.js +1 -1
  105. package/lib/object-file.js +1 -1
  106. package/lib/polaris/coverity-go.js +1 -1
  107. package/lib/polaris/coverity-java.js +1 -1
  108. package/lib/polaris/coverity-javascript.js +1 -1
  109. package/lib/polaris/coverity.js +1 -1
  110. package/lib/project-build.js +1 -1
  111. package/lib/project-tree.js +1 -1
  112. package/lib/project.js +1 -1
  113. package/lib/projects.js +1 -1
  114. package/lib/projenrc-json.js +1 -1
  115. package/lib/projenrc.js +1 -1
  116. package/lib/properties-file.js +1 -1
  117. package/lib/python/pip.js +1 -1
  118. package/lib/python/poetry.js +2 -2
  119. package/lib/python/projenrc.js +1 -1
  120. package/lib/python/pyproject-toml-file.js +1 -1
  121. package/lib/python/pytest-sample.js +1 -1
  122. package/lib/python/pytest.js +1 -1
  123. package/lib/python/python-project.js +1 -1
  124. package/lib/python/python-sample.js +1 -1
  125. package/lib/python/requirements-file.js +1 -1
  126. package/lib/python/setuppy.js +1 -1
  127. package/lib/python/setuptools.js +1 -1
  128. package/lib/python/uv-config.js +1 -1
  129. package/lib/python/uv.js +1 -1
  130. package/lib/python/venv.js +1 -1
  131. package/lib/readme.js +1 -1
  132. package/lib/release/publisher.js +1 -1
  133. package/lib/release/release-trigger.js +1 -1
  134. package/lib/release/release.js +1 -1
  135. package/lib/renovatebot.js +1 -1
  136. package/lib/run-task.cjs +1 -1
  137. package/lib/sample-file.js +2 -2
  138. package/lib/script-runner.js +1 -1
  139. package/lib/sonarqube/javascript.js +1 -1
  140. package/lib/sonarqube/rust.js +1 -1
  141. package/lib/sonarqube/sonarqube.js +1 -1
  142. package/lib/sonarqube/typescript.js +1 -1
  143. package/lib/source-code.js +1 -1
  144. package/lib/task-shell.js +1 -1
  145. package/lib/task.js +1 -1
  146. package/lib/tasks.js +1 -1
  147. package/lib/testing.js +1 -1
  148. package/lib/textfile.js +1 -1
  149. package/lib/toml.js +1 -1
  150. package/lib/typescript/projenrc-ts.js +1 -1
  151. package/lib/typescript/projenrc.js +1 -1
  152. package/lib/typescript/typescript-runner.js +1 -1
  153. package/lib/typescript/typescript-typedoc.js +1 -1
  154. package/lib/typescript/typescript.js +5 -5
  155. package/lib/version.js +2 -2
  156. package/lib/vscode/devcontainer.js +1 -1
  157. package/lib/vscode/extensions.js +1 -1
  158. package/lib/vscode/launch-config.js +1 -1
  159. package/lib/vscode/settings.js +1 -1
  160. package/lib/vscode/vscode.js +1 -1
  161. package/lib/web/next.js +3 -3
  162. package/lib/web/postcss.js +1 -1
  163. package/lib/web/react.js +3 -3
  164. package/lib/web/tailwind.js +1 -1
  165. package/lib/xmlfile.js +1 -1
  166. package/lib/yaml.js +1 -1
  167. package/package.json +1 -1
@@ -0,0 +1,234 @@
1
+ import type { IConstruct } from "constructs";
2
+ import { Component } from "../component";
3
+ import type { Project } from "../project";
4
+ import type { NodeConfigSchemaNodeOptions, NodeConfigSchemaTest } from "./node-config";
5
+ import { NodeConfigFile } from "./node-config-file";
6
+ import { NodeProject } from "./node-project";
7
+ /**
8
+ * Whether to update snapshots in task "test" (which is executed in task
9
+ * "build" and build workflows), or create a separate task "test:update" for
10
+ * updating snapshots.
11
+ */
12
+ export declare enum NodeTestUpdateSnapshot {
13
+ /**
14
+ * Always update snapshots in "test" task.
15
+ */
16
+ ALWAYS = "always",
17
+ /**
18
+ * Never update snapshots in "test" task and create a separate "test:update" task.
19
+ */
20
+ NEVER = "never"
21
+ }
22
+ export type ReporterKind = "dot" | "junit" | "lcov" | "spec" | "tap";
23
+ /**
24
+ * Where a reporter's output is written.
25
+ *
26
+ * @see https://nodejs.org/api/test.html#test-reporters
27
+ */
28
+ export declare class Destination {
29
+ /**
30
+ * The underlying value: `"stdout"`, `"stderr"`, or a file path.
31
+ */
32
+ readonly value: string;
33
+ /**
34
+ * Write to standard output.
35
+ */
36
+ static readonly STDOUT: Destination;
37
+ /**
38
+ * Write to standard error.
39
+ */
40
+ static readonly STDERR: Destination;
41
+ /**
42
+ * Write to a file at the given path.
43
+ * @param path path of the file to write to
44
+ */
45
+ static file(path: string): Destination;
46
+ private constructor();
47
+ }
48
+ /**
49
+ * A single reporter/destination pair for the Node.js native test runner.
50
+ *
51
+ * @see https://nodejs.org/api/test.html#test-reporters
52
+ */
53
+ export interface NodeReporter {
54
+ /**
55
+ * The name/kind of the reporter.
56
+ */
57
+ readonly name: ReporterKind;
58
+ /**
59
+ * Where the reporter's output is written.
60
+ *
61
+ * @see https://github.com/nodejs/node/blob/4215cc35e25c44f9f4fea5a4541afc862db7ef0a/test/parallel/test-runner-reporters.js#L46-L77
62
+ * @default Destination.STDOUT
63
+ */
64
+ readonly destination: Destination;
65
+ }
66
+ /**
67
+ * Options for Node.js' built-in test runner (`node --test`).
68
+ */
69
+ export interface NodeNativeTestRunnerOptions {
70
+ /**
71
+ * The directory where coverage files are output, if coverage collection
72
+ * is enabled.
73
+ *
74
+ * @default "coverage"
75
+ */
76
+ readonly coverageDirectory?: string;
77
+ /**
78
+ * Whether to update snapshots in task "test" (which is executed in task
79
+ * "build" and build workflows), or create a separate task "test:update"
80
+ * for updating snapshots.
81
+ *
82
+ * @default - ALWAYS
83
+ */
84
+ readonly updateSnapshot?: NodeTestUpdateSnapshot;
85
+ /**
86
+ * Additional options to pass to the `node --test` CLI invocation.
87
+ *
88
+ * Each element is passed as a single argument, exactly as given: no shell
89
+ * parses these, so a flag and its value need separate elements
90
+ * (`["--foo", "bar"]`, not `["--foo bar"]`).
91
+ *
92
+ * @default - no extra options
93
+ */
94
+ readonly extraCliOptions?: string[];
95
+ /**
96
+ * Preserve the default reporters (`spec`, `lcov`, `junit`) when additional
97
+ * reporters are added.
98
+ *
99
+ * @default true
100
+ */
101
+ readonly preserveDefaultReporters?: boolean;
102
+ /**
103
+ * Additional reporters to configure (e.g. `{ name: "tap", destination:
104
+ * Destination.file("test-reports/tap.txt") }`).
105
+ *
106
+ * These are added on top of the default reporters (`spec`, `lcov`, `junit`),
107
+ * which are controlled via `collectCoverage`. Use
108
+ * `NodeNativeTestRunner.addReporter`/`removeReporter`/`listReporters` to
109
+ * manage reporters after construction.
110
+ *
111
+ * @default - no additional reporters
112
+ */
113
+ readonly reporters?: NodeReporter[];
114
+ /**
115
+ * Path to the JSON configuration file for the test runner.
116
+ *
117
+ * @default "node.config.json"
118
+ */
119
+ readonly configFilePath?: string;
120
+ /**
121
+ * Glob patterns matching the files that contain tests. By default it
122
+ * combines Node.js' own default test file discovery with Jest conventions.
123
+ *
124
+ * @default - combines Node.js' own default test file discovery with Jest conventions
125
+ */
126
+ readonly testMatch?: string[];
127
+ /**
128
+ * Indicates whether the coverage information should be collected while
129
+ * executing the test, via `--experimental-test-coverage`.
130
+ *
131
+ * @default true
132
+ */
133
+ readonly collectCoverage?: boolean;
134
+ /**
135
+ * An array of glob patterns that are matched against all file paths before
136
+ * executing coverage collection. If a file path matches any of the
137
+ * patterns, coverage information will be skipped for it.
138
+ *
139
+ * @default ["**\/test/**", "**\/__tests__/**"]
140
+ */
141
+ readonly coveragePathIgnorePatterns?: string[];
142
+ /**
143
+ * This option allows the use of a custom global setup module which
144
+ * exports a function that is triggered once before all test suites.
145
+ * Written as `test-global-setup` in the generated Node.js configuration
146
+ * file.
147
+ *
148
+ * @default - undefined
149
+ */
150
+ readonly globalSetup?: string;
151
+ /**
152
+ * Enable module mocking support via `--experimental-test-module-mocks`.
153
+ *
154
+ * @default false
155
+ */
156
+ readonly moduleMocks?: boolean;
157
+ /**
158
+ * Whether to enable transformation of TypeScript-only syntax (e.g. enums, namespaces).
159
+ *
160
+ * Uses `amaro` (the TypeScript transformer used internally by Node.js) as an
161
+ * external loader via `--import=amaro/transform`. Adds a dependency on the
162
+ * `amaro` package and enables `--enable-source-maps` to preserve accurate
163
+ * stack traces.
164
+ *
165
+ * @see https://github.com/nodejs/amaro
166
+ * @default false
167
+ */
168
+ readonly transformTypes?: boolean;
169
+ /**
170
+ * Additional entries for the `nodeOptions` section of the generated
171
+ * configuration file (e.g. `enableSourceMaps`, `disableWarning`).
172
+ *
173
+ * @default - no additional node options
174
+ */
175
+ readonly nodeOptions?: NodeConfigSchemaNodeOptions;
176
+ /**
177
+ * Additional entries for the `test` section of the generated
178
+ * configuration file (e.g. `testConcurrency`, `testTimeout`).
179
+ *
180
+ * @default - no additional options
181
+ */
182
+ readonly testConfig?: NodeConfigSchemaTest;
183
+ }
184
+ /**
185
+ * Configures Node's built-in test runner (`node --test`).
186
+ *
187
+ * Owns the generated Node.js configuration file, the "test", "test:update"
188
+ * and "test:watch" tasks, and the reporters and test match patterns they use.
189
+ */
190
+ export declare class NodeNativeTestRunner extends Component {
191
+ /**
192
+ * Returns the singleton NodeNativeTestRunner component of a project or undefined
193
+ * if there is none.
194
+ */
195
+ static of(project: Project): NodeNativeTestRunner | undefined;
196
+ readonly project: NodeProject;
197
+ private readonly coverageDirectory?;
198
+ private readonly updateSnapshot;
199
+ private readonly reporters;
200
+ /**
201
+ * The generated Node.js configuration file.
202
+ */
203
+ readonly configFile: NodeConfigFile;
204
+ private readonly testReportsDir;
205
+ private readonly testMatch;
206
+ constructor(scope: IConstruct, options?: NodeNativeTestRunnerOptions);
207
+ /**
208
+ * Adds a test match pattern.
209
+ * @param pattern glob pattern to match for tests
210
+ */
211
+ addTestMatch(pattern: string): void;
212
+ /**
213
+ * Removes a test match pattern, if configured.
214
+ * @param pattern glob pattern to remove
215
+ */
216
+ removeTestMatch(pattern: string): void;
217
+ /**
218
+ * Adds a reporter, or updates its destination if one with the same name
219
+ * is already configured.
220
+ * @param name The name/kind of the reporter, e.g. `spec`, `junit`, `lcov`.
221
+ * @param destination Where the reporter's output is written.
222
+ * @default Destination.STDOUT
223
+ */
224
+ addReporter(name: ReporterKind, destination?: Destination): void;
225
+ /**
226
+ * Removes a reporter, if configured.
227
+ * @param name The name/kind of the reporter to remove.
228
+ */
229
+ removeReporter(name: ReporterKind): void;
230
+ /**
231
+ * Lists the configured reporters, in the order they were added.
232
+ */
233
+ listReporters(): NodeReporter[];
234
+ }
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeNativeTestRunner = exports.Destination = exports.NodeTestUpdateSnapshot = void 0;
4
+ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
5
+ const path_1 = require("path");
6
+ const component_1 = require("../component");
7
+ const node_config_file_1 = require("./node-config-file");
8
+ const node_project_1 = require("./node-project");
9
+ const util_1 = require("../util");
10
+ const constructs_1 = require("../util/constructs");
11
+ const DEFAULT_COVERAGE_DIR = "coverage";
12
+ const DEFAULT_TEST_REPORTS_DIR = "test-reports";
13
+ const DEFAULT_NODE_TEST_MATCH_EXTENSIONS = "cjs,mjs,js,jsx,cts,mts,ts,tsx";
14
+ /**
15
+ * Default glob patterns used to discover test files for Node's built-in test
16
+ * runner, combining Node.js' own default test file discovery with common
17
+ * Jest conventions, so that projects moving from Jest keep matching their
18
+ * existing test files.
19
+ *
20
+ * Node's test runner glob matching follows `glob(7)` and does not support
21
+ * extglobs (e.g. `@(...)`, `+(...)`, `?(...)`), so, unlike Jest's own
22
+ * defaults, each alternative is spelled out explicitly instead of relying on
23
+ * extended glob groups.
24
+ *
25
+ * @see https://nodejs.org/api/test.html#running-tests-from-the-command-line
26
+ */
27
+ const DEFAULT_NODE_TEST_MATCH = [
28
+ // Node.js' own defaults: files inside a "test" directory, or named/prefixed/suffixed "test"
29
+ `**/test/**/*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
30
+ `**/test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
31
+ `**/test-*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
32
+ `**/*.test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
33
+ `**/*-test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
34
+ `**/*_test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
35
+ // Jest conventions: files inside a "__tests__" directory, or named/suffixed "spec"
36
+ `**/__tests__/**/*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
37
+ `**/spec.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
38
+ `**/*.spec.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
39
+ ];
40
+ /**
41
+ * Whether to update snapshots in task "test" (which is executed in task
42
+ * "build" and build workflows), or create a separate task "test:update" for
43
+ * updating snapshots.
44
+ */
45
+ var NodeTestUpdateSnapshot;
46
+ (function (NodeTestUpdateSnapshot) {
47
+ /**
48
+ * Always update snapshots in "test" task.
49
+ */
50
+ NodeTestUpdateSnapshot["ALWAYS"] = "always";
51
+ /**
52
+ * Never update snapshots in "test" task and create a separate "test:update" task.
53
+ */
54
+ NodeTestUpdateSnapshot["NEVER"] = "never";
55
+ })(NodeTestUpdateSnapshot || (exports.NodeTestUpdateSnapshot = NodeTestUpdateSnapshot = {}));
56
+ /**
57
+ * Glob patterns matching the files that contain tests.
58
+ */
59
+ class TestMatch {
60
+ _patterns = new Set();
61
+ _defaultValue;
62
+ constructor(defaultValue) {
63
+ this._defaultValue = defaultValue;
64
+ }
65
+ add(pattern) {
66
+ this._patterns.add(pattern);
67
+ }
68
+ remove(pattern) {
69
+ this._patterns.delete(pattern);
70
+ }
71
+ /**
72
+ * Resolved lazily so patterns added or removed after construction are
73
+ * still reflected.
74
+ */
75
+ deferred() {
76
+ return this._patterns.size > 0
77
+ ? Array.from(this._patterns)
78
+ : this._defaultValue;
79
+ }
80
+ }
81
+ /**
82
+ * Where a reporter's output is written.
83
+ *
84
+ * @see https://nodejs.org/api/test.html#test-reporters
85
+ */
86
+ class Destination {
87
+ value;
88
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "projen.javascript.Destination", version: "0.103.27" };
89
+ /**
90
+ * Write to standard output.
91
+ */
92
+ static STDOUT = new Destination("stdout");
93
+ /**
94
+ * Write to standard error.
95
+ */
96
+ static STDERR = new Destination("stderr");
97
+ /**
98
+ * Write to a file at the given path.
99
+ * @param path path of the file to write to
100
+ */
101
+ static file(path) {
102
+ return new Destination(path);
103
+ }
104
+ constructor(
105
+ /**
106
+ * The underlying value: `"stdout"`, `"stderr"`, or a file path.
107
+ */
108
+ value) {
109
+ this.value = value;
110
+ }
111
+ }
112
+ exports.Destination = Destination;
113
+ /**
114
+ * The set of reporters configured for a `NodeNativeTestRunner`.
115
+ *
116
+ * Provides the `test.testReporter` and `test.testReporterDestination` values
117
+ * of the generated Node.js configuration file.
118
+ */
119
+ class NodeReporters {
120
+ project;
121
+ _reporters = new Map();
122
+ constructor(project) {
123
+ this.project = project;
124
+ }
125
+ /**
126
+ * Adds a reporter, or updates its destination if one with the same name
127
+ * is already configured.
128
+ * @param name The name/kind of the reporter, e.g. `spec`, `junit`, `lcov`.
129
+ * @param destination Where the reporter's output is written.
130
+ * @default Destination.STDOUT
131
+ */
132
+ add(name, destination = Destination.STDOUT) {
133
+ this._reporters.set(name, destination);
134
+ if (destination !== Destination.STDOUT &&
135
+ destination !== Destination.STDERR) {
136
+ this.ensureDestinationDirectory(destination);
137
+ }
138
+ }
139
+ /**
140
+ * Removes a reporter, if configured.
141
+ * @param name The name/kind of the reporter to remove.
142
+ */
143
+ remove(name) {
144
+ this._reporters.delete(name);
145
+ }
146
+ /**
147
+ * Lists the configured reporters, in the order they were added.
148
+ */
149
+ list() {
150
+ return Array.from(this._reporters, ([name, destination]) => ({
151
+ name,
152
+ destination,
153
+ }));
154
+ }
155
+ /**
156
+ * Ensures the directory of a reporter's file destination exists before the
157
+ * test runner is invoked, and excludes it from the project's `.gitignore`
158
+ * and package (e.g. `.npmignore`) files.
159
+ */
160
+ ensureDestinationDirectory(destination) {
161
+ const dir = path_1.posix.dirname(destination.value);
162
+ if (dir === ".") {
163
+ return;
164
+ }
165
+ // the reporter does not create its output directory, so it must exist
166
+ // before the test runner is invoked.
167
+ this.project.testTask.prependExec(`mkdir -p ${dir}`);
168
+ const dirPattern = path_1.posix.join("/", dir, "/");
169
+ this.project.addGitIgnore(dirPattern);
170
+ this.project.addPackageIgnore(dirPattern);
171
+ }
172
+ }
173
+ /**
174
+ * Configures Node's built-in test runner (`node --test`).
175
+ *
176
+ * Owns the generated Node.js configuration file, the "test", "test:update"
177
+ * and "test:watch" tasks, and the reporters and test match patterns they use.
178
+ */
179
+ class NodeNativeTestRunner extends component_1.Component {
180
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "projen.javascript.NodeNativeTestRunner", version: "0.103.27" };
181
+ /**
182
+ * Returns the singleton NodeNativeTestRunner component of a project or undefined
183
+ * if there is none.
184
+ */
185
+ static of(project) {
186
+ const isNodeNativeTestRunner = (c) => c instanceof NodeNativeTestRunner;
187
+ return project.components.find(isNodeNativeTestRunner);
188
+ }
189
+ project;
190
+ coverageDirectory;
191
+ updateSnapshot;
192
+ reporters;
193
+ /**
194
+ * The generated Node.js configuration file.
195
+ */
196
+ configFile;
197
+ testReportsDir = DEFAULT_TEST_REPORTS_DIR;
198
+ testMatch;
199
+ constructor(scope, options = {}) {
200
+ super(scope);
201
+ this.project = (0, constructs_1.closestProjectMustBe)(scope, node_project_1.NodeProject, new.target.name);
202
+ // Node's snapshot files are generated, not authored.
203
+ this.project.root.annotateGenerated("*.snap");
204
+ this.project.root.annotateGenerated("*.snapshot");
205
+ this.updateSnapshot =
206
+ options.updateSnapshot ?? NodeTestUpdateSnapshot.ALWAYS;
207
+ this.reporters = new NodeReporters(this.project);
208
+ const preserveDefaultReporters = options.preserveDefaultReporters ?? true;
209
+ this.testMatch = new TestMatch(DEFAULT_NODE_TEST_MATCH);
210
+ if (options.testMatch && options.testMatch.length > 0) {
211
+ options.testMatch.forEach((pattern) => this.testMatch.add(pattern));
212
+ }
213
+ const collectCoverage = options.collectCoverage ?? true;
214
+ const coverageDirectory = options.coverageDirectory ?? DEFAULT_COVERAGE_DIR;
215
+ this.coverageDirectory = collectCoverage ? coverageDirectory : undefined;
216
+ if (this.coverageDirectory) {
217
+ const coverageDirectoryPath = path_1.posix.join("/", this.coverageDirectory, "/");
218
+ this.project.addGitIgnore(coverageDirectoryPath);
219
+ this.project.addPackageIgnore(coverageDirectoryPath);
220
+ }
221
+ if (preserveDefaultReporters && collectCoverage) {
222
+ this.reporters.add("spec", Destination.STDOUT);
223
+ this.reporters.add("lcov", Destination.file(`${coverageDirectory}/lcov.info`));
224
+ this.reporters.add("junit", Destination.file(`${this.testReportsDir}/junit.xml`));
225
+ }
226
+ for (const { name, destination } of options.reporters ?? []) {
227
+ this.reporters.add(name, destination);
228
+ }
229
+ const defaultTest = {
230
+ test: true,
231
+ testGlobalSetup: options.globalSetup,
232
+ ...(collectCoverage
233
+ ? {
234
+ experimentalTestCoverage: true,
235
+ testCoverageExclude: options.coveragePathIgnorePatterns ?? [
236
+ "**/test/**",
237
+ "**/__tests__/**",
238
+ ],
239
+ }
240
+ : {}),
241
+ experimentalTestModuleMocks: options.moduleMocks,
242
+ // lazily resolved at synthesis time, so that reporters added or
243
+ // removed via `reporters` after construction are reflected in the
244
+ // generated file.
245
+ testReporter: (() => this.reporters.list().map((r) => r.name)),
246
+ testReporterDestination: (() => this.reporters.list().map((r) => r.destination.value)),
247
+ };
248
+ const test = (0, util_1.deepMerge)([defaultTest, options.testConfig ?? {}], { destructive: true });
249
+ let nodeOptions = { ...options.nodeOptions };
250
+ const extraCliOptions = options.extraCliOptions ?? [];
251
+ if (options.transformTypes ?? false) {
252
+ this.project.addDevDeps("amaro");
253
+ const imports = nodeOptions.import ?? [];
254
+ if (!imports.includes("amaro/transform")) {
255
+ imports.unshift("amaro/transform");
256
+ }
257
+ nodeOptions = {
258
+ ...nodeOptions,
259
+ enableSourceMaps: true,
260
+ import: imports,
261
+ };
262
+ }
263
+ this.configFile = new node_config_file_1.NodeConfigFile(this.project, {
264
+ filePath: options.configFilePath,
265
+ test,
266
+ nodeOptions,
267
+ });
268
+ this.project.npmignore?.addPatterns(`/${this.configFile.file.path}`);
269
+ // Test match patterns are positional arguments and must come after every
270
+ // flag: once Node sees the first positional argument it stops parsing
271
+ // flags, so any flag appended after them (e.g. `--watch`) is ignored.
272
+ const command = [
273
+ "node",
274
+ `--experimental-config-file=${this.configFile.file.path}`,
275
+ ...extraCliOptions,
276
+ ];
277
+ // `testMatch` is a live collection (`add`/`remove` can still be called
278
+ // after construction), so its resolution is deferred with `lazyExecArgs`
279
+ // rather than read eagerly here.
280
+ const testMatchArgs = () => this.testMatch.deferred();
281
+ if (this.updateSnapshot === NodeTestUpdateSnapshot.ALWAYS) {
282
+ this.project.testTask.execArgs(lazyExecArgs(() => [
283
+ ...command,
284
+ "--test-update-snapshots",
285
+ ...testMatchArgs(),
286
+ ]), { receiveArgs: true });
287
+ }
288
+ else {
289
+ const testUpdate = this.project.tasks.tryFind("test:update");
290
+ if (!testUpdate) {
291
+ this.project.addTask("test:update", {
292
+ description: "Update test snapshots",
293
+ execArgs: lazyExecArgs(() => [
294
+ ...command,
295
+ "--test-update-snapshots",
296
+ ...testMatchArgs(),
297
+ ]),
298
+ receiveArgs: true,
299
+ });
300
+ }
301
+ this.project.testTask.execArgs(lazyExecArgs(() => [...command, ...testMatchArgs()]), { receiveArgs: true });
302
+ }
303
+ const testWatch = this.project.tasks.tryFind("test:watch");
304
+ if (!testWatch) {
305
+ this.project.addTask("test:watch", {
306
+ description: "Run tests in watch mode",
307
+ execArgs: lazyExecArgs(() => [
308
+ ...command,
309
+ "--watch",
310
+ ...testMatchArgs(),
311
+ ]),
312
+ });
313
+ }
314
+ }
315
+ /**
316
+ * Adds a test match pattern.
317
+ * @param pattern glob pattern to match for tests
318
+ */
319
+ addTestMatch(pattern) {
320
+ this.testMatch.add(pattern);
321
+ }
322
+ /**
323
+ * Removes a test match pattern, if configured.
324
+ * @param pattern glob pattern to remove
325
+ */
326
+ removeTestMatch(pattern) {
327
+ this.testMatch.remove(pattern);
328
+ }
329
+ /**
330
+ * Adds a reporter, or updates its destination if one with the same name
331
+ * is already configured.
332
+ * @param name The name/kind of the reporter, e.g. `spec`, `junit`, `lcov`.
333
+ * @param destination Where the reporter's output is written.
334
+ * @default Destination.STDOUT
335
+ */
336
+ addReporter(name, destination) {
337
+ this.reporters.add(name, destination);
338
+ }
339
+ /**
340
+ * Removes a reporter, if configured.
341
+ * @param name The name/kind of the reporter to remove.
342
+ */
343
+ removeReporter(name) {
344
+ this.reporters.remove(name);
345
+ }
346
+ /**
347
+ * Lists the configured reporters, in the order they were added.
348
+ */
349
+ listReporters() {
350
+ return this.reporters.list();
351
+ }
352
+ }
353
+ exports.NodeNativeTestRunner = NodeNativeTestRunner;
354
+ /**
355
+ * Defers resolution of a `TaskStep`'s `execArgs` to synthesis time, when the
356
+ * tasks manifest is rendered.
357
+ *
358
+ * `TaskStep.execArgs` is typed as a plain `string[]`, but the manifest is
359
+ * written through the generic `resolve()` walker (see
360
+ * `ProjenTaskRunner.tasksManifest`) - the same mechanism `JsonFile`/`ObjectFile`
361
+ * use for lazy values elsewhere (e.g. `NodeReporters.list`, `TestMatch`).
362
+ * That walker calls any function it finds, so a callback deferred here runs
363
+ * once, at that point, in place of a plain array.
364
+ */
365
+ function lazyExecArgs(fn) {
366
+ return fn;
367
+ }
368
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZS1uYXRpdmUtdGVzdC1ydW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvamF2YXNjcmlwdC9ub2RlLW5hdGl2ZS10ZXN0LXJ1bm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsK0JBQTZCO0FBRTdCLDRDQUF5QztBQU16Qyx5REFBb0Q7QUFDcEQsaURBQTZDO0FBQzdDLGtDQUFvQztBQUNwQyxtREFBMEQ7QUFFMUQsTUFBTSxvQkFBb0IsR0FBRyxVQUFVLENBQUM7QUFFeEMsTUFBTSx3QkFBd0IsR0FBRyxjQUFjLENBQUM7QUFFaEQsTUFBTSxrQ0FBa0MsR0FBRywrQkFBK0IsQ0FBQztBQUUzRTs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLHVCQUF1QixHQUFhO0lBQ3hDLDRGQUE0RjtJQUM1RixpQkFBaUIsa0NBQWtDLEdBQUc7SUFDdEQsWUFBWSxrQ0FBa0MsR0FBRztJQUNqRCxjQUFjLGtDQUFrQyxHQUFHO0lBQ25ELGNBQWMsa0NBQWtDLEdBQUc7SUFDbkQsY0FBYyxrQ0FBa0MsR0FBRztJQUNuRCxjQUFjLGtDQUFrQyxHQUFHO0lBQ25ELG1GQUFtRjtJQUNuRixzQkFBc0Isa0NBQWtDLEdBQUc7SUFDM0QsWUFBWSxrQ0FBa0MsR0FBRztJQUNqRCxjQUFjLGtDQUFrQyxHQUFHO0NBQ3BELENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsSUFBWSxzQkFVWDtBQVZELFdBQVksc0JBQXNCO0lBQ2hDOztPQUVHO0lBQ0gsMkNBQWlCLENBQUE7SUFFakI7O09BRUc7SUFDSCx5Q0FBZSxDQUFBO0FBQ2pCLENBQUMsRUFWVyxzQkFBc0Isc0NBQXRCLHNCQUFzQixRQVVqQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxTQUFTO0lBQ0ksU0FBUyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDOUIsYUFBYSxDQUFXO0lBRXpDLFlBQVksWUFBc0I7UUFDaEMsSUFBSSxDQUFDLGFBQWEsR0FBRyxZQUFZLENBQUM7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IHsgSUNvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCI7XG5pbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tIFwiLi4vY29tcG9uZW50XCI7XG5pbXBvcnQgdHlwZSB7IFByb2plY3QgfSBmcm9tIFwiLi4vcHJvamVjdFwiO1xuaW1wb3J0IHR5cGUge1xuICBOb2RlQ29uZmlnU2NoZW1hTm9kZU9wdGlvbnMsXG4gIE5vZGVDb25maWdTY2hlbWFUZXN0LFxufSBmcm9tIFwiLi9ub2RlLWNvbmZpZ1wiO1xuaW1wb3J0IHsgTm9kZUNvbmZpZ0ZpbGUgfSBmcm9tIFwiLi9ub2RlLWNvbmZpZy1maWxlXCI7XG5pbXBvcnQgeyBOb2RlUHJvamVjdCB9IGZyb20gXCIuL25vZGUtcHJvamVjdFwiO1xuaW1wb3J0IHsgZGVlcE1lcmdlIH0gZnJvbSBcIi4uL3V0aWxcIjtcbmltcG9ydCB7IGNsb3Nlc3RQcm9qZWN0TXVzdEJlIH0gZnJvbSBcIi4uL3V0aWwvY29uc3RydWN0c1wiO1xuXG5jb25zdCBERUZBVUxUX0NPVkVSQUdFX0RJUiA9IFwiY292ZXJhZ2VcIjtcblxuY29uc3QgREVGQVVMVF9URVNUX1JFUE9SVFNfRElSID0gXCJ0ZXN0LXJlcG9ydHNcIjtcblxuY29uc3QgREVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OUyA9IFwiY2pzLG1qcyxqcyxqc3gsY3RzLG10cyx0cyx0c3hcIjtcblxuLyoqXG4gKiBEZWZhdWx0IGdsb2IgcGF0dGVybnMgdXNlZCB0byBkaXNjb3ZlciB0ZXN0IGZpbGVzIGZvciBOb2RlJ3MgYnVpbHQtaW4gdGVzdFxuICogcnVubmVyLCBjb21iaW5pbmcgTm9kZS5qcycgb3duIGRlZmF1bHQgdGVzdCBmaWxlIGRpc2NvdmVyeSB3aXRoIGNvbW1vblxuICogSmVzdCBjb252ZW50aW9ucywgc28gdGhhdCBwcm9qZWN0cyBtb3ZpbmcgZnJvbSBKZXN0IGtlZXAgbWF0Y2hpbmcgdGhlaXJcbiAqIGV4aXN0aW5nIHRlc3QgZmlsZXMuXG4gKlxuICogTm9kZSdzIHRlc3QgcnVubmVyIGdsb2IgbWF0Y2hpbmcgZm9sbG93cyBgZ2xvYig3KWAgYW5kIGRvZXMgbm90IHN1cHBvcnRcbiAqIGV4dGdsb2JzIChlLmcuIGBAKC4uLilgLCBgKyguLi4pYCwgYD8oLi4uKWApLCBzbywgdW5saWtlIEplc3QncyBvd25cbiAqIGRlZmF1bHRzLCBlYWNoIGFsdGVybmF0aXZlIGlzIHNwZWxsZWQgb3V0IGV4cGxpY2l0bHkgaW5zdGVhZCBvZiByZWx5aW5nIG9uXG4gKiBleHRlbmRlZCBnbG9iIGdyb3Vwcy5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvdGVzdC5odG1sI3J1bm5pbmctdGVzdHMtZnJvbS10aGUtY29tbWFuZC1saW5lXG4gKi9cbmNvbnN0IERFRkFVTFRfTk9ERV9URVNUX01BVENIOiBzdHJpbmdbXSA9IFtcbiAgLy8gTm9kZS5qcycgb3duIGRlZmF1bHRzOiBmaWxlcyBpbnNpZGUgYSBcInRlc3RcIiBkaXJlY3RvcnksIG9yIG5hbWVkL3ByZWZpeGVkL3N1ZmZpeGVkIFwidGVzdFwiXG4gIGAqKi90ZXN0LyoqLyoueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqL3Rlc3QueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqL3Rlc3QtKi57JHtERUZBVUxUX05PREVfVEVTVF9NQVRDSF9FWFRFTlNJT05TfX1gLFxuICBgKiovKi50ZXN0Lnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi8qLXRlc3QueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqLypfdGVzdC57JHtERUZBVUxUX05PREVfVEVTVF9NQVRDSF9FWFRFTlNJT05TfX1gLFxuICAvLyBKZXN0IGNvbnZlbnRpb25zOiBmaWxlcyBpbnNpZGUgYSBcIl9fdGVzdHNfX1wiIGRpcmVjdG9yeSwgb3IgbmFtZWQvc3VmZml4ZWQgXCJzcGVjXCJcbiAgYCoqL19fdGVzdHNfXy8qKi8qLnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi9zcGVjLnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi8qLnNwZWMueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbl07XG5cbi8qKlxuICogV2hldGhlciB0byB1cGRhdGUgc25hcHNob3RzIGluIHRhc2sgXCJ0ZXN0XCIgKHdoaWNoIGlzIGV4ZWN1dGVkIGluIHRhc2tcbiAqIFwiYnVpbGRcIiBhbmQgYnVpbGQgd29ya2Zsb3dzKSwgb3IgY3JlYXRlIGEgc2VwYXJhdGUgdGFzayBcInRlc3Q6dXBkYXRlXCIgZm9yXG4gKiB1cGRhdGluZyBzbmFwc2hvdHMuXG4gKi9cbmV4cG9ydCBlbnVtIE5vZGVUZXN0VXBkYXRlU25hcHNob3Qge1xuICAvKipcbiAgICogQWx3YXlzIHVwZGF0ZSBzbmFwc2hvdHMgaW4gXCJ0ZXN0XCIgdGFzay5cbiAgICovXG4gIEFMV0FZUyA9IFwiYWx3YXlzXCIsXG5cbiAgLyoqXG4gICAqIE5ldmVyIHVwZGF0ZSBzbmFwc2hvdHMgaW4gXCJ0ZXN0XCIgdGFzayBhbmQgY3JlYXRlIGEgc2VwYXJhdGUgXCJ0ZXN0OnVwZGF0ZVwiIHRhc2suXG4gICAqL1xuICBORVZFUiA9IFwibmV2ZXJcIixcbn1cblxuLyoqXG4gKiBHbG9iIHBhdHRlcm5zIG1hdGNoaW5nIHRoZSBmaWxlcyB0aGF0IGNvbnRhaW4gdGVzdHMuXG4gKi9cbmNsYXNzIFRlc3RNYXRjaCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3BhdHRlcm5zID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2RlZmF1bHRWYWx1ZTogc3RyaW5nW107XG5cbiAgY29uc3RydWN0b3IoZGVmYXVsdFZhbHVlOiBzdHJpbmdbXSkge1xuICAgIHRoaXMuX2RlZmF1bHRWYWx1ZSA9IGRlZmF1bHRWYWx1ZTtcbiAgfVxuXG4gIHB1YmxpYyBhZGQocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy5fcGF0dGVybnMuYWRkKHBhdHRlcm4pO1xuICB9XG5cbiAgcHVibGljIHJlbW92ZShwYXR0ZXJuOiBzdHJpbmcpIHtcbiAgICB0aGlzLl9wYXR0ZXJucy5kZWxldGUocGF0dGVybik7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZWQgbGF6aWx5IHNvIHBhdHRlcm5zIGFkZGVkIG9yIHJlbW92ZWQgYWZ0ZXIgY29uc3RydWN0aW9uIGFyZVxuICAgKiBzdGlsbCByZWZsZWN0ZWQuXG4gICAqL1xuICBwdWJsaWMgZGVmZXJyZWQoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLl9wYXR0ZXJucy5zaXplID4gMFxuICAgICAgPyBBcnJheS5mcm9tKHRoaXMuX3BhdHRlcm5zKVxuICAgICAgOiB0aGlzLl9kZWZhdWx0VmFsdWU7XG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgUmVwb3J0ZXJLaW5kID0gXCJkb3RcIiB8IFwianVuaXRcIiB8IFwibGNvdlwiIHwgXCJzcGVjXCIgfCBcInRhcFwiO1xuXG4vKipcbiAqIFdoZXJlIGEgcmVwb3J0ZXIncyBvdXRwdXQgaXMgd3JpdHRlbi5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvdGVzdC5odG1sI3Rlc3QtcmVwb3J0ZXJzXG4gKi9cbmV4cG9ydCBjbGFzcyBEZXN0aW5hdGlvbiB7XG4gIC8qKlxuICAgKiBXcml0ZSB0byBzdGFuZGFyZCBvdXRwdXQuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IFNURE9VVCA9IG5ldyBEZXN0aW5hdGlvbihcInN0ZG91dFwiKTtcblxuICAvKipcbiAgICogV3JpdGUgdG8gc3RhbmRhcmQgZXJyb3IuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IFNUREVSUiA9IG5ldyBEZXN0aW5hdGlvbihcInN0ZGVyclwiKTtcblxuICAvKipcbiAgICogV3JpdGUgdG8gYSBmaWxlIGF0IHRoZSBnaXZlbiBwYXRoLlxuICAgKiBAcGFyYW0gcGF0aCBwYXRoIG9mIHRoZSBmaWxlIHRvIHdyaXRlIHRvXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGZpbGUocGF0aDogc3RyaW5nKTogRGVzdGluYXRpb24ge1xuICAgIHJldHVybiBuZXcgRGVzdGluYXRpb24ocGF0aCk7XG4gIH1cblxuICBwcml2YXRlIGNvbnN0cnVjdG9yKFxuICAgIC8qKlxuICAgICAqIFRoZSB1bmRlcmx5aW5nIHZhbHVlOiBgXCJzdGRvdXRcImAsIGBcInN0ZGVyclwiYCwgb3IgYSBmaWxlIHBhdGguXG4gICAgICovXG4gICAgcHVibGljIHJlYWRvbmx5IHZhbHVlOiBzdHJpbmcsXG4gICkge31cbn1cblxuLyoqXG4gKiBBIHNpbmdsZSByZXBvcnRlci9kZXN0aW5hdGlvbiBwYWlyIGZvciB0aGUgTm9kZS5qcyBuYXRpdmUgdGVzdCBydW5uZXIuXG4gKlxuICogQHNlZSBodHRwczovL25vZGVqcy5vcmcvYXBpL3Rlc3QuaHRtbCN0ZXN0LXJlcG9ydGVyc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIE5vZGVSZXBvcnRlciB7XG4gIC8qKlxuICAgKiBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlci5cbiAgICovXG4gIHJlYWRvbmx5IG5hbWU6IFJlcG9ydGVyS2luZDtcbiAgLyoqXG4gICAqIFdoZXJlIHRoZSByZXBvcnRlcidzIG91dHB1dCBpcyB3cml0dGVuLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvbm9kZS9ibG9iLzQyMTVjYzM1ZTI1YzQ0ZjlmNGZlYTVhNDU0MWFmYzg2MmRiN2VmMGEvdGVzdC9wYXJhbGxlbC90ZXN0LXJ1bm5lci1yZXBvcnRlcnMuanMjTDQ2LUw3N1xuICAgKiBAZGVmYXVsdCBEZXN0aW5hdGlvbi5TVERPVVRcbiAgICovXG4gIHJlYWRvbmx5IGRlc3RpbmF0aW9uOiBEZXN0aW5hdGlvbjtcbn1cblxuLyoqXG4gKiBUaGUgc2V0IG9mIHJlcG9ydGVycyBjb25maWd1cmVkIGZvciBhIGBOb2RlTmF0aXZlVGVzdFJ1bm5lcmAuXG4gKlxuICogUHJvdmlkZXMgdGhlIGB0ZXN0LnRlc3RSZXBvcnRlcmAgYW5kIGB0ZXN0LnRlc3RSZXBvcnRlckRlc3RpbmF0aW9uYCB2YWx1ZXNcbiAqIG9mIHRoZSBnZW5lcmF0ZWQgTm9kZS5qcyBjb25maWd1cmF0aW9uIGZpbGUuXG4gKi9cbmNsYXNzIE5vZGVSZXBvcnRlcnMge1xuICBwcml2YXRlIHJlYWRvbmx5IF9yZXBvcnRlcnM6IE1hcDxSZXBvcnRlcktpbmQsIERlc3RpbmF0aW9uPiA9IG5ldyBNYXAoKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHByb2plY3Q6IE5vZGVQcm9qZWN0KSB7fVxuXG4gIC8qKlxuICAgKiBBZGRzIGEgcmVwb3J0ZXIsIG9yIHVwZGF0ZXMgaXRzIGRlc3RpbmF0aW9uIGlmIG9uZSB3aXRoIHRoZSBzYW1lIG5hbWVcbiAgICogaXMgYWxyZWFkeSBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciwgZS5nLiBgc3BlY2AsIGBqdW5pdGAsIGBsY292YC5cbiAgICogQHBhcmFtIGRlc3RpbmF0aW9uIFdoZXJlIHRoZSByZXBvcnRlcidzIG91dHB1dCBpcyB3cml0dGVuLlxuICAgKiBAZGVmYXVsdCBEZXN0aW5hdGlvbi5TVERPVVRcbiAgICovXG4gIHB1YmxpYyBhZGQoXG4gICAgbmFtZTogUmVwb3J0ZXJLaW5kLFxuICAgIGRlc3RpbmF0aW9uOiBEZXN0aW5hdGlvbiA9IERlc3RpbmF0aW9uLlNURE9VVCxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5fcmVwb3J0ZXJzLnNldChuYW1lLCBkZXN0aW5hdGlvbik7XG5cbiAgICBpZiAoXG4gICAgICBkZXN0aW5hdGlvbiAhPT0gRGVzdGluYXRpb24uU1RET1VUICYmXG4gICAgICBkZXN0aW5hdGlvbiAhPT0gRGVzdGluYXRpb24uU1RERVJSXG4gICAgKSB7XG4gICAgICB0aGlzLmVuc3VyZURlc3RpbmF0aW9uRGlyZWN0b3J5KGRlc3RpbmF0aW9uKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlcG9ydGVyLCBpZiBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciB0byByZW1vdmUuXG4gICAqL1xuICBwdWJsaWMgcmVtb3ZlKG5hbWU6IFJlcG9ydGVyS2luZCk6IHZvaWQge1xuICAgIHRoaXMuX3JlcG9ydGVycy5kZWxldGUobmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgdGhlIGNvbmZpZ3VyZWQgcmVwb3J0ZXJzLCBpbiB0aGUgb3JkZXIgdGhleSB3ZXJlIGFkZGVkLlxuICAgKi9cbiAgcHVibGljIGxpc3QoKTogTm9kZVJlcG9ydGVyW10ge1xuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuX3JlcG9ydGVycywgKFtuYW1lLCBkZXN0aW5hdGlvbl0pID0+ICh7XG4gICAgICBuYW1lLFxuICAgICAgZGVzdGluYXRpb24sXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZXMgdGhlIGRpcmVjdG9yeSBvZiBhIHJlcG9ydGVyJ3MgZmlsZSBkZXN0aW5hdGlvbiBleGlzdHMgYmVmb3JlIHRoZVxuICAgKiB0ZXN0IHJ1bm5lciBpcyBpbnZva2VkLCBhbmQgZXhjbHVkZXMgaXQgZnJvbSB0aGUgcHJvamVjdCdzIGAuZ2l0aWdub3JlYFxuICAgKiBhbmQgcGFja2FnZSAoZS5nLiBgLm5wbWlnbm9yZWApIGZpbGVzLlxuICAgKi9cbiAgcHJpdmF0ZSBlbnN1cmVEZXN0aW5hdGlvbkRpcmVjdG9yeShkZXN0aW5hdGlvbjogRGVzdGluYXRpb24pOiB2b2lkIHtcbiAgICBjb25zdCBkaXIgPSBwb3NpeC5kaXJuYW1lKGRlc3RpbmF0aW9uLnZhbHVlKTtcbiAgICBpZiAoZGlyID09PSBcIi5cIikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyB0aGUgcmVwb3J0ZXIgZG9lcyBub3QgY3JlYXRlIGl0cyBvdXRwdXQgZGlyZWN0b3J5LCBzbyBpdCBtdXN0IGV4aXN0XG4gICAgLy8gYmVmb3JlIHRoZSB0ZXN0IHJ1bm5lciBpcyBpbnZva2VkLlxuICAgIHRoaXMucHJvamVjdC50ZXN0VGFzay5wcmVwZW5kRXhlYyhgbWtkaXIgLXAgJHtkaXJ9YCk7XG5cbiAgICBjb25zdCBkaXJQYXR0ZXJuID0gcG9zaXguam9pbihcIi9cIiwgZGlyLCBcIi9cIik7XG4gICAgdGhpcy5wcm9qZWN0LmFkZEdpdElnbm9yZShkaXJQYXR0ZXJuKTtcbiAgICB0aGlzLnByb2plY3QuYWRkUGFja2FnZUlnbm9yZShkaXJQYXR0ZXJuKTtcbiAgfVxufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIE5vZGUuanMnIGJ1aWx0LWluIHRlc3QgcnVubmVyIChgbm9kZSAtLXRlc3RgKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBOb2RlTmF0aXZlVGVzdFJ1bm5lck9wdGlvbnMge1xuICAvKipcbiAgICogVGhlIGRpcmVjdG9yeSB3aGVyZSBjb3ZlcmFnZSBmaWxlcyBhcmUgb3V0cHV0LCBpZiBjb3ZlcmFnZSBjb2xsZWN0aW9uXG4gICAqIGlzIGVuYWJsZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IFwiY292ZXJhZ2VcIlxuICAgKi9cbiAgcmVhZG9ubHkgY292ZXJhZ2VEaXJlY3Rvcnk/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gdXBkYXRlIHNuYXBzaG90cyBpbiB0YXNrIFwidGVzdFwiICh3aGljaCBpcyBleGVjdXRlZCBpbiB0YXNrXG4gICAqIFwiYnVpbGRcIiBhbmQgYnVpbGQgd29ya2Zsb3dzKSwgb3IgY3JlYXRlIGEgc2VwYXJhdGUgdGFzayBcInRlc3Q6dXBkYXRlXCJcbiAgICogZm9yIHVwZGF0aW5nIHNuYXBzaG90cy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBBTFdBWVNcbiAgICovXG4gIHJlYWRvbmx5IHVwZGF0ZVNuYXBzaG90PzogTm9kZVRlc3RVcGRhdGVTbmFwc2hvdDtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBvcHRpb25zIHRvIHBhc3MgdG8gdGhlIGBub2RlIC0tdGVzdGAgQ0xJIGludm9jYXRpb24uXG4gICAqXG4gICAqIEVhY2ggZWxlbWVudCBpcyBwYXNzZWQgYXMgYSBzaW5nbGUgYXJndW1lbnQsIGV4YWN0bHkgYXMgZ2l2ZW46IG5vIHNoZWxsXG4gICAqIHBhcnNlcyB0aGVzZSwgc28gYSBmbGFnIGFuZCBpdHMgdmFsdWUgbmVlZCBzZXBhcmF0ZSBlbGVtZW50c1xuICAgKiAoYFtcIi0tZm9vXCIsIFwiYmFyXCJdYCwgbm90IGBbXCItLWZvbyBiYXJcIl1gKS5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBleHRyYSBvcHRpb25zXG4gICAqL1xuICByZWFkb25seSBleHRyYUNsaU9wdGlvbnM/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogUHJlc2VydmUgdGhlIGRlZmF1bHQgcmVwb3J0ZXJzIChgc3BlY2AsIGBsY292YCwgYGp1bml0YCkgd2hlbiBhZGRpdGlvbmFsXG4gICAqIHJlcG9ydGVycyBhcmUgYWRkZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IHRydWVcbiAgICovXG4gIHJlYWRvbmx5IHByZXNlcnZlRGVmYXVsdFJlcG9ydGVycz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwgcmVwb3J0ZXJzIHRvIGNvbmZpZ3VyZSAoZS5nLiBgeyBuYW1lOiBcInRhcFwiLCBkZXN0aW5hdGlvbjpcbiAgICogRGVzdGluYXRpb24uZmlsZShcInRlc3QtcmVwb3J0cy90YXAudHh0XCIpIH1gKS5cbiAgICpcbiAgICogVGhlc2UgYXJlIGFkZGVkIG9uIHRvcCBvZiB0aGUgZGVmYXVsdCByZXBvcnRlcnMgKGBzcGVjYCwgYGxjb3ZgLCBganVuaXRgKSxcbiAgICogd2hpY2ggYXJlIGNvbnRyb2xsZWQgdmlhIGBjb2xsZWN0Q292ZXJhZ2VgLiBVc2VcbiAgICogYE5vZGVOYXRpdmVUZXN0UnVubmVyLmFkZFJlcG9ydGVyYC9gcmVtb3ZlUmVwb3J0ZXJgL2BsaXN0UmVwb3J0ZXJzYCB0b1xuICAgKiBtYW5hZ2UgcmVwb3J0ZXJzIGFmdGVyIGNvbnN0cnVjdGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBhZGRpdGlvbmFsIHJlcG9ydGVyc1xuICAgKi9cbiAgcmVhZG9ubHkgcmVwb3J0ZXJzPzogTm9kZVJlcG9ydGVyW107XG5cbiAgLyoqXG4gICAqIFBhdGggdG8gdGhlIEpTT04gY29uZmlndXJhdGlvbiBmaWxlIGZvciB0aGUgdGVzdCBydW5uZXIuXG4gICAqXG4gICAqIEBkZWZhdWx0IFwibm9kZS5jb25maWcuanNvblwiXG4gICAqL1xuICByZWFkb25seSBjb25maWdGaWxlUGF0aD86IHN0cmluZztcblxuICAvKipcbiAgICogR2xvYiBwYXR0ZXJucyBtYXRjaGluZyB0aGUgZmlsZXMgdGhhdCBjb250YWluIHRlc3RzLiBCeSBkZWZhdWx0IGl0XG4gICAqIGNvbWJpbmVzIE5vZGUuanMnIG93biBkZWZhdWx0IHRlc3QgZmlsZSBkaXNjb3Zlcnkgd2l0aCBKZXN0IGNvbnZlbnRpb25zLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGNvbWJpbmVzIE5vZGUuanMnIG93biBkZWZhdWx0IHRlc3QgZmlsZSBkaXNjb3Zlcnkgd2l0aCBKZXN0IGNvbnZlbnRpb25zXG4gICAqL1xuICByZWFkb25seSB0ZXN0TWF0Y2g/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogSW5kaWNhdGVzIHdoZXRoZXIgdGhlIGNvdmVyYWdlIGluZm9ybWF0aW9uIHNob3VsZCBiZSBjb2xsZWN0ZWQgd2hpbGVcbiAgICogZXhlY3V0aW5nIHRoZSB0ZXN0LCB2aWEgYC0tZXhwZXJpbWVudGFsLXRlc3QtY292ZXJhZ2VgLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBjb2xsZWN0Q292ZXJhZ2U/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBBbiBhcnJheSBvZiBnbG9iIHBhdHRlcm5zIHRoYXQgYXJlIG1hdGNoZWQgYWdhaW5zdCBhbGwgZmlsZSBwYXRocyBiZWZvcmVcbiAgICogZXhlY3V0aW5nIGNvdmVyYWdlIGNvbGxlY3Rpb24uIElmIGEgZmlsZSBwYXRoIG1hdGNoZXMgYW55IG9mIHRoZVxuICAgKiBwYXR0ZXJucywgY292ZXJhZ2UgaW5mb3JtYXRpb24gd2lsbCBiZSBza2lwcGVkIGZvciBpdC5cbiAgICpcbiAgICogQGRlZmF1bHQgW1wiKipcXC90ZXN0LyoqXCIsIFwiKipcXC9fX3Rlc3RzX18vKipcIl1cbiAgICovXG4gIHJlYWRvbmx5IGNvdmVyYWdlUGF0aElnbm9yZVBhdHRlcm5zPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIFRoaXMgb3B0aW9uIGFsbG93cyB0aGUgdXNlIG9mIGEgY3VzdG9tIGdsb2JhbCBzZXR1cCBtb2R1bGUgd2hpY2hcbiAgICogZXhwb3J0cyBhIGZ1bmN0aW9uIHRoYXQgaXMgdHJpZ2dlcmVkIG9uY2UgYmVmb3JlIGFsbCB0ZXN0IHN1aXRlcy5cbiAgICogV3JpdHRlbiBhcyBgdGVzdC1nbG9iYWwtc2V0dXBgIGluIHRoZSBnZW5lcmF0ZWQgTm9kZS5qcyBjb25maWd1cmF0aW9uXG4gICAqIGZpbGUuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdW5kZWZpbmVkXG4gICAqL1xuICByZWFkb25seSBnbG9iYWxTZXR1cD86IHN0cmluZztcblxuICAvKipcbiAgICogRW5hYmxlIG1vZHVsZSBtb2NraW5nIHN1cHBvcnQgdmlhIGAtLWV4cGVyaW1lbnRhbC10ZXN0LW1vZHVsZS1tb2Nrc2AuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBtb2R1bGVNb2Nrcz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gZW5hYmxlIHRyYW5zZm9ybWF0aW9uIG9mIFR5cGVTY3JpcHQtb25seSBzeW50YXggKGUuZy4gZW51bXMsIG5hbWVzcGFjZXMpLlxuICAgKlxuICAgKiBVc2VzIGBhbWFyb2AgKHRoZSBUeXBlU2NyaXB0IHRyYW5zZm9ybWVyIHVzZWQgaW50ZXJuYWxseSBieSBOb2RlLmpzKSBhcyBhblxuICAgKiBleHRlcm5hbCBsb2FkZXIgdmlhIGAtLWltcG9ydD1hbWFyby90cmFuc2Zvcm1gLiBBZGRzIGEgZGVwZW5kZW5jeSBvbiB0aGVcbiAgICogYGFtYXJvYCBwYWNrYWdlIGFuZCBlbmFibGVzIGAtLWVuYWJsZS1zb3VyY2UtbWFwc2AgdG8gcHJlc2VydmUgYWNjdXJhdGVcbiAgICogc3RhY2sgdHJhY2VzLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvYW1hcm9cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IHRyYW5zZm9ybVR5cGVzPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBlbnRyaWVzIGZvciB0aGUgYG5vZGVPcHRpb25zYCBzZWN0aW9uIG9mIHRoZSBnZW5lcmF0ZWRcbiAgICogY29uZmlndXJhdGlvbiBmaWxlIChlLmcuIGBlbmFibGVTb3VyY2VNYXBzYCwgYGRpc2FibGVXYXJuaW5nYCkuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gYWRkaXRpb25hbCBub2RlIG9wdGlvbnNcbiAgICovXG4gIHJlYWRvbmx5IG5vZGVPcHRpb25zPzogTm9kZUNvbmZpZ1NjaGVtYU5vZGVPcHRpb25zO1xuXG4gIC8qKlxuICAgKiBBZGRpdGlvbmFsIGVudHJpZXMgZm9yIHRoZSBgdGVzdGAgc2VjdGlvbiBvZiB0aGUgZ2VuZXJhdGVkXG4gICAqIGNvbmZpZ3VyYXRpb24gZmlsZSAoZS5nLiBgdGVzdENvbmN1cnJlbmN5YCwgYHRlc3RUaW1lb3V0YCkuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gYWRkaXRpb25hbCBvcHRpb25zXG4gICAqL1xuICByZWFkb25seSB0ZXN0Q29uZmlnPzogTm9kZUNvbmZpZ1NjaGVtYVRlc3Q7XG59XG5cbi8qKlxuICogQ29uZmlndXJlcyBOb2RlJ3MgYnVpbHQtaW4gdGVzdCBydW5uZXIgKGBub2RlIC0tdGVzdGApLlxuICpcbiAqIE93bnMgdGhlIGdlbmVyYXRlZCBOb2RlLmpzIGNvbmZpZ3VyYXRpb24gZmlsZSwgdGhlIFwidGVzdFwiLCBcInRlc3Q6dXBkYXRlXCJcbiAqIGFuZCBcInRlc3Q6d2F0Y2hcIiB0YXNrcywgYW5kIHRoZSByZXBvcnRlcnMgYW5kIHRlc3QgbWF0Y2ggcGF0dGVybnMgdGhleSB1c2UuXG4gKi9cbmV4cG9ydCBjbGFzcyBOb2RlTmF0aXZlVGVzdFJ1bm5lciBleHRlbmRzIENvbXBvbmVudCB7XG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBzaW5nbGV0b24gTm9kZU5hdGl2ZVRlc3RSdW5uZXIgY29tcG9uZW50IG9mIGEgcHJvamVjdCBvciB1bmRlZmluZWRcbiAgICogaWYgdGhlcmUgaXMgbm9uZS5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgb2YocHJvamVjdDogUHJvamVjdCk6IE5vZGVOYXRpdmVUZXN0UnVubmVyIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBpc05vZGVOYXRpdmVUZXN0UnVubmVyID0gKGM6IENvbXBvbmVudCk6IGMgaXMgTm9kZU5hdGl2ZVRlc3RSdW5uZXIgPT5cbiAgICAgIGMgaW5zdGFuY2VvZiBOb2RlTmF0aXZlVGVzdFJ1bm5lcjtcbiAgICByZXR1cm4gcHJvamVjdC5jb21wb25lbnRzLmZpbmQoaXNOb2RlTmF0aXZlVGVzdFJ1bm5lcik7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgcHJvamVjdDogTm9kZVByb2plY3Q7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBjb3ZlcmFnZURpcmVjdG9yeT86IHN0cmluZztcblxuICBwcml2YXRlIHJlYWRvbmx5IHVwZGF0ZVNuYXBzaG90OiBOb2RlVGVzdFVwZGF0ZVNuYXBzaG90O1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgcmVwb3J0ZXJzOiBOb2RlUmVwb3J0ZXJzO1xuXG4gIC8qKlxuICAgKiBUaGUgZ2VuZXJhdGVkIE5vZGUuanMgY29uZmlndXJhdGlvbiBmaWxlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNvbmZpZ0ZpbGU6IE5vZGVDb25maWdGaWxlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgdGVzdFJlcG9ydHNEaXI6IHN0cmluZyA9IERFRkFVTFRfVEVTVF9SRVBPUlRTX0RJUjtcblxuICBwcml2YXRlIHJlYWRvbmx5IHRlc3RNYXRjaDogVGVzdE1hdGNoO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBJQ29uc3RydWN0LCBvcHRpb25zOiBOb2RlTmF0aXZlVGVzdFJ1bm5lck9wdGlvbnMgPSB7fSkge1xuICAgIHN1cGVyKHNjb3BlKTtcbiAgICB0aGlzLnByb2plY3QgPSBjbG9zZXN0UHJvamVjdE11c3RCZShzY29wZSwgTm9kZVByb2plY3QsIG5ldy50YXJnZXQubmFtZSk7XG5cbiAgICAvLyBOb2RlJ3Mgc25hcHNob3QgZmlsZXMgYXJlIGdlbmVyYXRlZCwgbm90IGF1dGhvcmVkLlxuICAgIHRoaXMucHJvamVjdC5yb290LmFubm90YXRlR2VuZXJhdGVkKFwiKi5zbmFwXCIpO1xuICAgIHRoaXMucHJvamVjdC5yb290LmFubm90YXRlR2VuZXJhdGVkKFwiKi5zbmFwc2hvdFwiKTtcblxuICAgIHRoaXMudXBkYXRlU25hcHNob3QgPVxuICAgICAgb3B0aW9ucy51cGRhdGVTbmFwc2hvdCA/PyBOb2RlVGVzdFVwZGF0ZVNuYXBzaG90LkFMV0FZUztcblxuICAgIHRoaXMucmVwb3J0ZXJzID0gbmV3IE5vZGVSZXBvcnRlcnModGhpcy5wcm9qZWN0KTtcbiAgICBjb25zdCBwcmVzZXJ2ZURlZmF1bHRSZXBvcnRlcnMgPSBvcHRpb25zLnByZXNlcnZlRGVmYXVsdFJlcG9ydGVycyA/PyB0cnVlO1xuXG4gICAgdGhpcy50ZXN0TWF0Y2ggPSBuZXcgVGVzdE1hdGNoKERFRkFVTFRfTk9ERV9URVNUX01BVENIKTtcblxuICAgIGlmIChvcHRpb25zLnRlc3RNYXRjaCAmJiBvcHRpb25zLnRlc3RNYXRjaC5sZW5ndGggPiAwKSB7XG4gICAgICBvcHRpb25zLnRlc3RNYXRjaC5mb3JFYWNoKChwYXR0ZXJuKSA9PiB0aGlzLnRlc3RNYXRjaC5hZGQocGF0dGVybikpO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbGxlY3RDb3ZlcmFnZSA9IG9wdGlvbnMuY29sbGVjdENvdmVyYWdlID8/IHRydWU7XG4gICAgY29uc3QgY292ZXJhZ2VEaXJlY3RvcnkgPSBvcHRpb25zLmNvdmVyYWdlRGlyZWN0b3J5ID8/IERFRkFVTFRfQ09WRVJBR0VfRElSO1xuICAgIHRoaXMuY292ZXJhZ2VEaXJlY3RvcnkgPSBjb2xsZWN0Q292ZXJhZ2UgPyBjb3ZlcmFnZURpcmVjdG9yeSA6IHVuZGVmaW5lZDtcblxuICAgIGlmICh0aGlzLmNvdmVyYWdlRGlyZWN0b3J5KSB7XG4gICAgICBjb25zdCBjb3ZlcmFnZURpcmVjdG9yeVBhdGggPSBwb3NpeC5qb2luKFxuICAgICAgICBcIi9cIixcbiAgICAgICAgdGhpcy5jb3ZlcmFnZURpcmVjdG9yeSxcbiAgICAgICAgXCIvXCIsXG4gICAgICApO1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZEdpdElnbm9yZShjb3ZlcmFnZURpcmVjdG9yeVBhdGgpO1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZFBhY2thZ2VJZ25vcmUoY292ZXJhZ2VEaXJlY3RvcnlQYXRoKTtcbiAgICB9XG5cbiAgICBpZiAocHJlc2VydmVEZWZhdWx0UmVwb3J0ZXJzICYmIGNvbGxlY3RDb3ZlcmFnZSkge1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFwic3BlY1wiLCBEZXN0aW5hdGlvbi5TVERPVVQpO1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFxuICAgICAgICBcImxjb3ZcIixcbiAgICAgICAgRGVzdGluYXRpb24uZmlsZShgJHtjb3ZlcmFnZURpcmVjdG9yeX0vbGNvdi5pbmZvYCksXG4gICAgICApO1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFxuICAgICAgICBcImp1bml0XCIsXG4gICAgICAgIERlc3RpbmF0aW9uLmZpbGUoYCR7dGhpcy50ZXN0UmVwb3J0c0Rpcn0vanVuaXQueG1sYCksXG4gICAgICApO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgeyBuYW1lLCBkZXN0aW5hdGlvbiB9IG9mIG9wdGlvbnMucmVwb3J0ZXJzID8/IFtdKSB7XG4gICAgICB0aGlzLnJlcG9ydGVycy5hZGQobmFtZSwgZGVzdGluYXRpb24pO1xuICAgIH1cblxuICAgIGNvbnN0IGRlZmF1bHRUZXN0OiBOb2RlQ29uZmlnU2NoZW1hVGVzdCA9IHtcbiAgICAgIHRlc3Q6IHRydWUsXG4gICAgICB0ZXN0R2xvYmFsU2V0dXA6IG9wdGlvbnMuZ2xvYmFsU2V0dXAsXG4gICAgICAuLi4oY29sbGVjdENvdmVyYWdlXG4gICAgICAgID8ge1xuICAgICAgICAgICAgZXhwZXJpbWVudGFsVGVzdENvdmVyYWdlOiB0cnVlLFxuICAgICAgICAgICAgdGVzdENvdmVyYWdlRXhjbHVkZTogb3B0aW9ucy5jb3ZlcmFnZVBhdGhJZ25vcmVQYXR0ZXJucyA/PyBbXG4gICAgICAgICAgICAgIFwiKiovdGVzdC8qKlwiLFxuICAgICAgICAgICAgICBcIioqL19fdGVzdHNfXy8qKlwiLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICB9XG4gICAgICAgIDoge30pLFxuICAgICAgZXhwZXJpbWVudGFsVGVzdE1vZHVsZU1vY2tzOiBvcHRpb25zLm1vZHVsZU1vY2tzLFxuICAgICAgLy8gbGF6aWx5IHJlc29sdmVkIGF0IHN5bnRoZXNpcyB0aW1lLCBzbyB0aGF0IHJlcG9ydGVycyBhZGRlZCBvclxuICAgICAgLy8gcmVtb3ZlZCB2aWEgYHJlcG9ydGVyc2AgYWZ0ZXIgY29uc3RydWN0aW9uIGFyZSByZWZsZWN0ZWQgaW4gdGhlXG4gICAgICAvLyBnZW5lcmF0ZWQgZmlsZS5cbiAgICAgIHRlc3RSZXBvcnRlcjogKCgpID0+IHRoaXMucmVwb3J0ZXJzLmxpc3QoKS5tYXAoKHIpID0+IHIubmFtZSkpIGFzIGFueSxcbiAgICAgIHRlc3RSZXBvcnRlckRlc3RpbmF0aW9uOiAoKCkgPT5cbiAgICAgICAgdGhpcy5yZXBvcnRlcnMubGlzdCgpLm1hcCgocikgPT4gci5kZXN0aW5hdGlvbi52YWx1ZSkpIGFzIGFueSxcbiAgICB9O1xuICAgIGNvbnN0IHRlc3Q6IE5vZGVDb25maWdTY2hlbWFUZXN0ID0gZGVlcE1lcmdlKFxuICAgICAgW2RlZmF1bHRUZXN0LCBvcHRpb25zLnRlc3RDb25maWcgPz8ge31dLFxuICAgICAgeyBkZXN0cnVjdGl2ZTogdHJ1ZSB9LFxuICAgICk7XG5cbiAgICBsZXQgbm9kZU9wdGlvbnM6IE5vZGVDb25maWdTY2hlbWFOb2RlT3B0aW9ucyA9IHsgLi4ub3B0aW9ucy5ub2RlT3B0aW9ucyB9O1xuICAgIGNvbnN0IGV4dHJhQ2xpT3B0aW9ucyA9IG9wdGlvbnMuZXh0cmFDbGlPcHRpb25zID8/IFtdO1xuXG4gICAgaWYgKG9wdGlvbnMudHJhbnNmb3JtVHlwZXMgPz8gZmFsc2UpIHtcbiAgICAgIHRoaXMucHJvamVjdC5hZGREZXZEZXBzKFwiYW1hcm9cIik7XG4gICAgICBjb25zdCBpbXBvcnRzID0gbm9kZU9wdGlvbnMuaW1wb3J0ID8/IFtdO1xuICAgICAgaWYgKCFpbXBvcnRzLmluY2x1ZGVzKFwiYW1hcm8vdHJhbnNmb3JtXCIpKSB7XG4gICAgICAgIGltcG9ydHMudW5zaGlmdChcImFtYXJvL3RyYW5zZm9ybVwiKTtcbiAgICAgIH1cbiAgICAgIG5vZGVPcHRpb25zID0ge1xuICAgICAgICAuLi5ub2RlT3B0aW9ucyxcbiAgICAgICAgZW5hYmxlU291cmNlTWFwczogdHJ1ZSxcbiAgICAgICAgaW1wb3J0OiBpbXBvcnRzLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbmZpZ0ZpbGUgPSBuZXcgTm9kZUNvbmZpZ0ZpbGUodGhpcy5wcm9qZWN0LCB7XG4gICAgICBmaWxlUGF0aDogb3B0aW9ucy5jb25maWdGaWxlUGF0aCxcbiAgICAgIHRlc3QsXG4gICAgICBub2RlT3B0aW9ucyxcbiAgICB9KTtcbiAgICB0aGlzLnByb2plY3QubnBtaWdub3JlPy5hZGRQYXR0ZXJucyhgLyR7dGhpcy5jb25maWdGaWxlLmZpbGUucGF0aH1gKTtcblxuICAgIC8vIFRlc3QgbWF0Y2ggcGF0dGVybnMgYXJlIHBvc2l0aW9uYWwgYXJndW1lbnRzIGFuZCBtdXN0IGNvbWUgYWZ0ZXIgZXZlcnlcbiAgICAvLyBmbGFnOiBvbmNlIE5vZGUgc2VlcyB0aGUgZmlyc3QgcG9zaXRpb25hbCBhcmd1bWVudCBpdCBzdG9wcyBwYXJzaW5nXG4gICAgLy8gZmxhZ3MsIHNvIGFueSBmbGFnIGFwcGVuZGVkIGFmdGVyIHRoZW0gKGUuZy4gYC0td2F0Y2hgKSBpcyBpZ25vcmVkLlxuICAgIGNvbnN0IGNvbW1hbmQ6IHN0cmluZ1tdID0gW1xuICAgICAgXCJub2RlXCIsXG4gICAgICBgLS1leHBlcmltZW50YWwtY29uZmlnLWZpbGU9JHt0aGlzLmNvbmZpZ0ZpbGUuZmlsZS5wYXRofWAsXG4gICAgICAuLi5leHRyYUNsaU9wdGlvbnMsXG4gICAgXTtcblxuICAgIC8vIGB0ZXN0TWF0Y2hgIGlzIGEgbGl2ZSBjb2xsZWN0aW9uIChgYWRkYC9gcmVtb3ZlYCBjYW4gc3RpbGwgYmUgY2FsbGVkXG4gICAgLy8gYWZ0ZXIgY29uc3RydWN0aW9uKSwgc28gaXRzIHJlc29sdXRpb24gaXMgZGVmZXJyZWQgd2l0aCBgbGF6eUV4ZWNBcmdzYFxuICAgIC8vIHJhdGhlciB0aGFuIHJlYWQgZWFnZXJseSBoZXJlLlxuICAgIGNvbnN0IHRlc3RNYXRjaEFyZ3MgPSAoKSA9PiB0aGlzLnRlc3RNYXRjaC5kZWZlcnJlZCgpO1xuXG4gICAgaWYgKHRoaXMudXBkYXRlU25hcHNob3QgPT09IE5vZGVUZXN0VXBkYXRlU25hcHNob3QuQUxXQVlTKSB7XG4gICAgICB0aGlzLnByb2plY3QudGVzdFRhc2suZXhlY0FyZ3MoXG4gICAgICAgIGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgLi4uY29tbWFuZCxcbiAgICAgICAgICBcIi0tdGVzdC11cGRhdGUtc25hcHNob3RzXCIsXG4gICAgICAgICAgLi4udGVzdE1hdGNoQXJncygpLFxuICAgICAgICBdKSxcbiAgICAgICAgeyByZWNlaXZlQXJnczogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgdGVzdFVwZGF0ZSA9IHRoaXMucHJvamVjdC50YXNrcy50cnlGaW5kKFwidGVzdDp1cGRhdGVcIik7XG4gICAgICBpZiAoIXRlc3RVcGRhdGUpIHtcbiAgICAgICAgdGhpcy5wcm9qZWN0LmFkZFRhc2soXCJ0ZXN0OnVwZGF0ZVwiLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246IFwiVXBkYXRlIHRlc3Qgc25hcHNob3RzXCIsXG4gICAgICAgICAgZXhlY0FyZ3M6IGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgICAuLi5jb21tYW5kLFxuICAgICAgICAgICAgXCItLXRlc3QtdXBkYXRlLXNuYXBzaG90c1wiLFxuICAgICAgICAgICAgLi4udGVzdE1hdGNoQXJncygpLFxuICAgICAgICAgIF0pLFxuICAgICAgICAgIHJlY2VpdmVBcmdzOiB0cnVlLFxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5wcm9qZWN0LnRlc3RUYXNrLmV4ZWNBcmdzKFxuICAgICAgICBsYXp5RXhlY0FyZ3MoKCkgPT4gWy4uLmNvbW1hbmQsIC4uLnRlc3RNYXRjaEFyZ3MoKV0pLFxuICAgICAgICB7IHJlY2VpdmVBcmdzOiB0cnVlIH0sXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHRlc3RXYXRjaCA9IHRoaXMucHJvamVjdC50YXNrcy50cnlGaW5kKFwidGVzdDp3YXRjaFwiKTtcbiAgICBpZiAoIXRlc3RXYXRjaCkge1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZFRhc2soXCJ0ZXN0OndhdGNoXCIsIHtcbiAgICAgICAgZGVzY3JpcHRpb246IFwiUnVuIHRlc3RzIGluIHdhdGNoIG1vZGVcIixcbiAgICAgICAgZXhlY0FyZ3M6IGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgLi4uY29tbWFuZCxcbiAgICAgICAgICBcIi0td2F0Y2hcIixcbiAgICAgICAgICAuLi50ZXN0TWF0Y2hBcmdzKCksXG4gICAgICAgIF0pLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSB0ZXN0IG1hdGNoIHBhdHRlcm4uXG4gICAqIEBwYXJhbSBwYXR0ZXJuIGdsb2IgcGF0dGVybiB0byBtYXRjaCBmb3IgdGVzdHNcbiAgICovXG4gIHB1YmxpYyBhZGRUZXN0TWF0Y2gocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy50ZXN0TWF0Y2guYWRkKHBhdHRlcm4pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSB0ZXN0IG1hdGNoIHBhdHRlcm4sIGlmIGNvbmZpZ3VyZWQuXG4gICAqIEBwYXJhbSBwYXR0ZXJuIGdsb2IgcGF0dGVybiB0byByZW1vdmVcbiAgICovXG4gIHB1YmxpYyByZW1vdmVUZXN0TWF0Y2gocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy50ZXN0TWF0Y2gucmVtb3ZlKHBhdHRlcm4pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSByZXBvcnRlciwgb3IgdXBkYXRlcyBpdHMgZGVzdGluYXRpb24gaWYgb25lIHdpdGggdGhlIHNhbWUgbmFtZVxuICAgKiBpcyBhbHJlYWR5IGNvbmZpZ3VyZWQuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lL2tpbmQgb2YgdGhlIHJlcG9ydGVyLCBlLmcuIGBzcGVjYCwgYGp1bml0YCwgYGxjb3ZgLlxuICAgKiBAcGFyYW0gZGVzdGluYXRpb24gV2hlcmUgdGhlIHJlcG9ydGVyJ3Mgb3V0cHV0IGlzIHdyaXR0ZW4uXG4gICAqIEBkZWZhdWx0IERlc3RpbmF0aW9uLlNURE9VVFxuICAgKi9cbiAgcHVibGljIGFkZFJlcG9ydGVyKG5hbWU6IFJlcG9ydGVyS2luZCwgZGVzdGluYXRpb24/OiBEZXN0aW5hdGlvbikge1xuICAgIHRoaXMucmVwb3J0ZXJzLmFkZChuYW1lLCBkZXN0aW5hdGlvbik7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlcG9ydGVyLCBpZiBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciB0byByZW1vdmUuXG4gICAqL1xuICBwdWJsaWMgcmVtb3ZlUmVwb3J0ZXIobmFtZTogUmVwb3J0ZXJLaW5kKSB7XG4gICAgdGhpcy5yZXBvcnRlcnMucmVtb3ZlKG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIExpc3RzIHRoZSBjb25maWd1cmVkIHJlcG9ydGVycywgaW4gdGhlIG9yZGVyIHRoZXkgd2VyZSBhZGRlZC5cbiAgICovXG4gIHB1YmxpYyBsaXN0UmVwb3J0ZXJzKCk6IE5vZGVSZXBvcnRlcltdIHtcbiAgICByZXR1cm4gdGhpcy5yZXBvcnRlcnMubGlzdCgpO1xuICB9XG59XG5cbi8qKlxuICogRGVmZXJzIHJlc29sdXRpb24gb2YgYSBgVGFza1N0ZXBgJ3MgYGV4ZWNBcmdzYCB0byBzeW50aGVzaXMgdGltZSwgd2hlbiB0aGVcbiAqIHRhc2tzIG1hbmlmZXN0IGlzIHJlbmRlcmVkLlxuICpcbiAqIGBUYXNrU3RlcC5leGVjQXJnc2AgaXMgdHlwZWQgYXMgYSBwbGFpbiBgc3RyaW5nW11gLCBidXQgdGhlIG1hbmlmZXN0IGlzXG4gKiB3cml0dGVuIHRocm91Z2ggdGhlIGdlbmVyaWMgYHJlc29sdmUoKWAgd2Fsa2VyIChzZWVcbiAqIGBQcm9qZW5UYXNrUnVubmVyLnRhc2tzTWFuaWZlc3RgKSAtIHRoZSBzYW1lIG1lY2hhbmlzbSBgSnNvbkZpbGVgL2BPYmplY3RGaWxlYFxuICogdXNlIGZvciBsYXp5IHZhbHVlcyBlbHNld2hlcmUgKGUuZy4gYE5vZGVSZXBvcnRlcnMubGlzdGAsIGBUZXN0TWF0Y2hgKS5cbiAqIFRoYXQgd2Fsa2VyIGNhbGxzIGFueSBmdW5jdGlvbiBpdCBmaW5kcywgc28gYSBjYWxsYmFjayBkZWZlcnJlZCBoZXJlIHJ1bnNcbiAqIG9uY2UsIGF0IHRoYXQgcG9pbnQsIGluIHBsYWNlIG9mIGEgcGxhaW4gYXJyYXkuXG4gKi9cbmZ1bmN0aW9uIGxhenlFeGVjQXJncyhmbjogKCkgPT4gc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBmbiBhcyBhbnk7XG59XG4iXX0=