@jsenv/core 32.2.4 → 33.0.0

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.
package/dist/jsenv.js CHANGED
@@ -540,7 +540,7 @@ const moveUrl = ({
540
540
  url,
541
541
  from,
542
542
  to,
543
- preferAbsolute = false
543
+ preferRelative
544
544
  }) => {
545
545
  let relativeUrl = urlToRelativeUrl(url, from);
546
546
  if (relativeUrl.slice(0, 2) === "//") {
@@ -548,10 +548,10 @@ const moveUrl = ({
548
548
  relativeUrl = new URL(relativeUrl, url).href;
549
549
  }
550
550
  const absoluteUrl = new URL(relativeUrl, to).href;
551
- if (preferAbsolute) {
552
- return absoluteUrl;
551
+ if (preferRelative) {
552
+ return urlToRelativeUrl(absoluteUrl, to);
553
553
  }
554
- return urlToRelativeUrl(absoluteUrl, to);
554
+ return absoluteUrl;
555
555
  };
556
556
 
557
557
  const urlIsInsideOf = (url, otherUrl) => {
@@ -7106,7 +7106,8 @@ const watchSourceFiles = (sourceDirectoryUrl, callback, {
7106
7106
  // by default watch everything inside the source directory
7107
7107
  // line below is commented until @jsenv/url-meta fixes the fact that is matches
7108
7108
  // any file with an extension
7109
- // "**/.*": false, // file starting with a dot -> do not watch
7109
+ "**/.*": false,
7110
+ // file starting with a dot -> do not watch
7110
7111
  "**/.*/": false,
7111
7112
  // directory starting with a dot -> do not watch
7112
7113
  "**/node_modules/": false,
@@ -9588,8 +9589,7 @@ const determineFileUrlForOutDirectory = ({
9588
9589
  return moveUrl({
9589
9590
  url,
9590
9591
  from: context.rootDirectoryUrl,
9591
- to: context.outDirectoryUrl,
9592
- preferAbsolute: true
9592
+ to: context.outDirectoryUrl
9593
9593
  });
9594
9594
  };
9595
9595
 
@@ -22748,8 +22748,7 @@ const inferParentFromRequest = (request, sourceDirectoryUrl) => {
22748
22748
  return moveUrl({
22749
22749
  url: referer,
22750
22750
  from: `${request.origin}/`,
22751
- to: sourceDirectoryUrl,
22752
- preferAbsolute: true
22751
+ to: sourceDirectoryUrl
22753
22752
  });
22754
22753
  };
22755
22754
 
@@ -23124,6 +23123,72 @@ const generateCoverageTextLog = (coverage, {
23124
23123
  report.execute(context);
23125
23124
  };
23126
23125
 
23126
+ const executionStepsFromTestPlan = async ({
23127
+ signal,
23128
+ rootDirectoryUrl,
23129
+ testPlan
23130
+ }) => {
23131
+ try {
23132
+ const fileResultArray = await collectFiles({
23133
+ signal,
23134
+ directoryUrl: rootDirectoryUrl,
23135
+ associations: {
23136
+ testPlan
23137
+ },
23138
+ predicate: ({
23139
+ testPlan
23140
+ }) => testPlan
23141
+ });
23142
+ const executionSteps = [];
23143
+ fileResultArray.forEach(({
23144
+ relativeUrl,
23145
+ meta
23146
+ }) => {
23147
+ const fileExecutionSteps = generateFileExecutionSteps({
23148
+ fileRelativeUrl: relativeUrl,
23149
+ filePlan: meta.testPlan
23150
+ });
23151
+ executionSteps.push(...fileExecutionSteps);
23152
+ });
23153
+ return executionSteps;
23154
+ } catch (e) {
23155
+ if (Abort.isAbortError(e)) {
23156
+ return {
23157
+ aborted: true,
23158
+ planSummary: {},
23159
+ planReport: {},
23160
+ planCoverage: null
23161
+ };
23162
+ }
23163
+ throw e;
23164
+ }
23165
+ };
23166
+ const generateFileExecutionSteps = ({
23167
+ fileRelativeUrl,
23168
+ filePlan
23169
+ }) => {
23170
+ const fileExecutionSteps = [];
23171
+ Object.keys(filePlan).forEach(executionName => {
23172
+ const stepConfig = filePlan[executionName];
23173
+ if (stepConfig === null || stepConfig === undefined) {
23174
+ return;
23175
+ }
23176
+ if (typeof stepConfig !== "object") {
23177
+ throw new TypeError(createDetailedMessage$1(`found unexpected value in plan, they must be object`, {
23178
+ ["file relative path"]: fileRelativeUrl,
23179
+ ["execution name"]: executionName,
23180
+ ["value"]: stepConfig
23181
+ }));
23182
+ }
23183
+ fileExecutionSteps.push({
23184
+ executionName,
23185
+ fileRelativeUrl,
23186
+ ...stepConfig
23187
+ });
23188
+ });
23189
+ return fileExecutionSteps;
23190
+ };
23191
+
23127
23192
  const readNodeV8CoverageDirectory = async ({
23128
23193
  logger,
23129
23194
  signal,
@@ -23781,59 +23846,6 @@ const ensureGlobalGc = () => {
23781
23846
  }
23782
23847
  };
23783
23848
 
23784
- const generateExecutionSteps = async (plan, {
23785
- signal,
23786
- rootDirectoryUrl
23787
- }) => {
23788
- const fileResultArray = await collectFiles({
23789
- signal,
23790
- directoryUrl: rootDirectoryUrl,
23791
- associations: {
23792
- filePlan: plan
23793
- },
23794
- predicate: ({
23795
- filePlan
23796
- }) => filePlan
23797
- });
23798
- const executionSteps = [];
23799
- fileResultArray.forEach(({
23800
- relativeUrl,
23801
- meta
23802
- }) => {
23803
- const fileExecutionSteps = generateFileExecutionSteps({
23804
- fileRelativeUrl: relativeUrl,
23805
- filePlan: meta.filePlan
23806
- });
23807
- executionSteps.push(...fileExecutionSteps);
23808
- });
23809
- return executionSteps;
23810
- };
23811
- const generateFileExecutionSteps = ({
23812
- fileRelativeUrl,
23813
- filePlan
23814
- }) => {
23815
- const fileExecutionSteps = [];
23816
- Object.keys(filePlan).forEach(executionName => {
23817
- const stepConfig = filePlan[executionName];
23818
- if (stepConfig === null || stepConfig === undefined) {
23819
- return;
23820
- }
23821
- if (typeof stepConfig !== "object") {
23822
- throw new TypeError(createDetailedMessage$1(`found unexpected value in plan, they must be object`, {
23823
- ["file relative path"]: fileRelativeUrl,
23824
- ["execution name"]: executionName,
23825
- ["value"]: stepConfig
23826
- }));
23827
- }
23828
- fileExecutionSteps.push({
23829
- executionName,
23830
- fileRelativeUrl,
23831
- ...stepConfig
23832
- });
23833
- });
23834
- return fileExecutionSteps;
23835
- };
23836
-
23837
23849
  const EXECUTION_COLORS = {
23838
23850
  executing: ANSI.BLUE,
23839
23851
  aborted: ANSI.MAGENTA,
@@ -24174,7 +24186,7 @@ ${key}: ${details[key]}`;
24174
24186
  return message;
24175
24187
  };
24176
24188
 
24177
- const executePlan = async (plan, {
24189
+ const executeSteps = async (executionSteps, {
24178
24190
  signal,
24179
24191
  handleSIGINT,
24180
24192
  logger,
@@ -24189,6 +24201,7 @@ const executePlan = async (plan, {
24189
24201
  completedExecutionLogAbbreviation,
24190
24202
  rootDirectoryUrl,
24191
24203
  devServerOrigin,
24204
+ sourceDirectoryUrl,
24192
24205
  keepRunning,
24193
24206
  defaultMsAllocatedPerExecution,
24194
24207
  maxExecutionsInParallel,
@@ -24268,19 +24281,13 @@ const executePlan = async (plan, {
24268
24281
  let runtimeParams = {
24269
24282
  rootDirectoryUrl,
24270
24283
  devServerOrigin,
24284
+ sourceDirectoryUrl,
24271
24285
  coverageEnabled,
24272
24286
  coverageConfig,
24273
24287
  coverageMethodForBrowsers,
24274
24288
  coverageMethodForNodeJs,
24275
24289
  stopAfterAllSignal
24276
24290
  };
24277
- logger.debug(`Generate executions`);
24278
- const executionSteps = await getExecutionAsSteps({
24279
- plan,
24280
- multipleExecutionsOperation,
24281
- rootDirectoryUrl
24282
- });
24283
- logger.debug(`${executionSteps.length} executions planned`);
24284
24291
  if (completedExecutionLogMerging && !process.stdout.isTTY) {
24285
24292
  completedExecutionLogMerging = false;
24286
24293
  logger.debug(`Force completedExecutionLogMerging to false because process.stdout.isTTY is false`);
@@ -24497,29 +24504,6 @@ const executePlan = async (plan, {
24497
24504
  await multipleExecutionsOperation.end();
24498
24505
  }
24499
24506
  };
24500
- const getExecutionAsSteps = async ({
24501
- plan,
24502
- multipleExecutionsOperation,
24503
- rootDirectoryUrl
24504
- }) => {
24505
- try {
24506
- const executionSteps = await generateExecutionSteps(plan, {
24507
- signal: multipleExecutionsOperation.signal,
24508
- rootDirectoryUrl
24509
- });
24510
- return executionSteps;
24511
- } catch (e) {
24512
- if (Abort.isAbortError(e)) {
24513
- return {
24514
- aborted: true,
24515
- planSummary: {},
24516
- planReport: {},
24517
- planCoverage: null
24518
- };
24519
- }
24520
- throw e;
24521
- }
24522
- };
24523
24507
  const canOverwriteLogGetter = ({
24524
24508
  completedExecutionLogMerging,
24525
24509
  executionResult
@@ -24586,9 +24570,9 @@ const executeInParallel = async ({
24586
24570
  /**
24587
24571
  * Execute a list of files and log how it goes.
24588
24572
  * @param {Object} testPlanParameters
24589
- * @param {string|url} testPlanParameters.testDirectoryUrl Directory containing test files
24573
+ * @param {string|url} testPlanParameters.rootDirectoryUrl Directory containing test files;
24590
24574
  * @param {string|url} [testPlanParameters.devServerOrigin=undefined] Jsenv dev server origin; required when executing test on browsers
24591
- * @param {Object} testPlanParameters.testPlan Object associating patterns leading to files to runtimes where they should be executed
24575
+ * @param {Object} testPlanParameters.testPlan Object associating files with runtimes where they will be executed
24592
24576
  * @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
24593
24577
  * @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
24594
24578
  * @param {number} [testPlanParameters.maxExecutionsInParallel=1] Maximum amount of execution in parallel
@@ -24613,7 +24597,7 @@ const executeTestPlan = async ({
24613
24597
  logFileRelativeUrl = ".jsenv/test_plan_debug.txt",
24614
24598
  completedExecutionLogAbbreviation = false,
24615
24599
  completedExecutionLogMerging = false,
24616
- testDirectoryUrl,
24600
+ rootDirectoryUrl,
24617
24601
  devServerModuleUrl,
24618
24602
  devServerOrigin,
24619
24603
  testPlan,
@@ -24631,7 +24615,14 @@ const executeTestPlan = async ({
24631
24615
  gcBetweenExecutions = logMemoryHeapUsage,
24632
24616
  coverageEnabled = process.argv.includes("--coverage"),
24633
24617
  coverageConfig = {
24634
- "./**/*": true
24618
+ "**/*": true,
24619
+ "**/.*": false,
24620
+ "**/.*/": false,
24621
+ "**/node_modules/": false,
24622
+ "**/tests/": false,
24623
+ "**/*.test.html": false,
24624
+ "**/*.test.js": false,
24625
+ "**/*.test.mjs": false
24635
24626
  },
24636
24627
  coverageIncludeMissing = true,
24637
24628
  coverageAndExecutionAllowed = false,
@@ -24640,7 +24631,6 @@ const executeTestPlan = async ({
24640
24631
  // "istanbul" also accepted
24641
24632
  coverageV8ConflictWarning = true,
24642
24633
  coverageTempDirectoryUrl,
24643
- coverageReportRootDirectoryUrl,
24644
24634
  // skip empty means empty files won't appear in the coverage reports (json and html)
24645
24635
  coverageReportSkipEmpty = false,
24646
24636
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
@@ -24655,6 +24645,7 @@ const executeTestPlan = async ({
24655
24645
  let someNeedsServer = false;
24656
24646
  let someNodeRuntime = false;
24657
24647
  let stopDevServerNeeded = false;
24648
+ let sourceDirectoryUrl;
24658
24649
  const runtimes = {};
24659
24650
  // param validation
24660
24651
  {
@@ -24662,9 +24653,9 @@ const executeTestPlan = async ({
24662
24653
  if (unexpectedParamNames.length > 0) {
24663
24654
  throw new TypeError(`${unexpectedParamNames.join(",")}: there is no such param`);
24664
24655
  }
24665
- testDirectoryUrl = assertAndNormalizeDirectoryUrl(testDirectoryUrl, "testDirectoryUrl");
24666
- if (!existsSync(new URL(testDirectoryUrl))) {
24667
- throw new Error(`ENOENT on testDirectoryUrl at ${testDirectoryUrl}`);
24656
+ rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl, "rootDirectoryUrl");
24657
+ if (!existsSync(new URL(rootDirectoryUrl))) {
24658
+ throw new Error(`ENOENT on rootDirectoryUrl at ${rootDirectoryUrl}`);
24668
24659
  }
24669
24660
  if (typeof testPlan !== "object") {
24670
24661
  throw new Error(`testPlan must be an object, got ${testPlan}`);
@@ -24713,14 +24704,10 @@ const executeTestPlan = async ({
24713
24704
  }
24714
24705
  stopDevServerNeeded = true;
24715
24706
  }
24716
- const {
24717
- sourceDirectoryUrl
24718
- } = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
24707
+ const devServerParams = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
24719
24708
  rejectUnauthorized: false
24720
24709
  });
24721
- if (testDirectoryUrl !== sourceDirectoryUrl && !urlIsInsideOf(testDirectoryUrl, sourceDirectoryUrl)) {
24722
- throw new Error(`testDirectoryUrl must be inside sourceDirectoryUrl when running tests on browser(s)`);
24723
- }
24710
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl;
24724
24711
  }
24725
24712
  if (coverageEnabled) {
24726
24713
  if (typeof coverageConfig !== "object") {
@@ -24749,26 +24736,21 @@ const executeTestPlan = async ({
24749
24736
  }));
24750
24737
  }
24751
24738
  }
24752
- if (coverageReportRootDirectoryUrl === undefined) {
24753
- coverageReportRootDirectoryUrl = lookupPackageDirectory(testDirectoryUrl);
24754
- } else {
24755
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportRootDirectoryUrl, "coverageReportRootDirectoryUrl");
24756
- }
24757
24739
  if (coverageTempDirectoryUrl === undefined) {
24758
- coverageTempDirectoryUrl = new URL("./.coverage/tmp/", coverageReportRootDirectoryUrl);
24740
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl);
24759
24741
  } else {
24760
24742
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageTempDirectoryUrl, "coverageTempDirectoryUrl");
24761
24743
  }
24762
24744
  if (coverageReportJson) {
24763
24745
  if (coverageReportJsonFileUrl === undefined) {
24764
- coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", coverageReportRootDirectoryUrl);
24746
+ coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", rootDirectoryUrl);
24765
24747
  } else {
24766
24748
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(coverageReportJsonFileUrl, "coverageReportJsonFileUrl");
24767
24749
  }
24768
24750
  }
24769
24751
  if (coverageReportHtml) {
24770
24752
  if (coverageReportHtmlDirectoryUrl === undefined) {
24771
- coverageReportHtmlDirectoryUrl = new URL("./.coverage/", coverageReportRootDirectoryUrl);
24753
+ coverageReportHtmlDirectoryUrl = new URL("./.coverage/", rootDirectoryUrl);
24772
24754
  } else {
24773
24755
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportHtmlDirectoryUrl, "coverageReportHtmlDirectoryUrl");
24774
24756
  }
@@ -24806,7 +24788,14 @@ const executeTestPlan = async ({
24806
24788
  ...testPlan,
24807
24789
  "**/.jsenv/": null
24808
24790
  };
24809
- const result = await executePlan(testPlan, {
24791
+ logger.debug(`Generate executions`);
24792
+ const executionSteps = await executionStepsFromTestPlan({
24793
+ signal,
24794
+ testPlan,
24795
+ rootDirectoryUrl
24796
+ });
24797
+ logger.debug(`${executionSteps.length} executions planned`);
24798
+ const result = await executeSteps(executionSteps, {
24810
24799
  signal,
24811
24800
  handleSIGINT,
24812
24801
  logger,
@@ -24819,8 +24808,9 @@ const executeTestPlan = async ({
24819
24808
  logFileRelativeUrl,
24820
24809
  completedExecutionLogMerging,
24821
24810
  completedExecutionLogAbbreviation,
24822
- rootDirectoryUrl: testDirectoryUrl,
24811
+ rootDirectoryUrl,
24823
24812
  devServerOrigin,
24813
+ sourceDirectoryUrl,
24824
24814
  maxExecutionsInParallel,
24825
24815
  defaultMsAllocatedPerExecution,
24826
24816
  failFast,
@@ -24861,8 +24851,8 @@ const executeTestPlan = async ({
24861
24851
  const htmlCoverageDirectoryIndexFileUrl = `${coverageReportHtmlDirectoryUrl}index.html`;
24862
24852
  logger.info(`-> ${urlToFileSystemPath(htmlCoverageDirectoryIndexFileUrl)}`);
24863
24853
  promises.push(generateCoverageHtmlDirectory(planCoverage, {
24864
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
24865
- coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, coverageReportRootDirectoryUrl),
24854
+ rootDirectoryUrl,
24855
+ coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, rootDirectoryUrl),
24866
24856
  coverageReportSkipEmpty,
24867
24857
  coverageReportSkipFull
24868
24858
  }));
@@ -24910,6 +24900,7 @@ const createRuntimeFromPlaywright = ({
24910
24900
  rootDirectoryUrl,
24911
24901
  fileRelativeUrl,
24912
24902
  devServerOrigin,
24903
+ sourceDirectoryUrl,
24913
24904
  // measurePerformance,
24914
24905
  collectPerformance,
24915
24906
  coverageEnabled = false,
@@ -25016,8 +25007,7 @@ const createRuntimeFromPlaywright = ({
25016
25007
  const fsUrl = moveUrl({
25017
25008
  url: v8CoveragesWithWebUrl.url,
25018
25009
  from: `${devServerOrigin}/`,
25019
- to: rootDirectoryUrl,
25020
- preferAbsolute: true
25010
+ to: sourceDirectoryUrl
25021
25011
  });
25022
25012
  return {
25023
25013
  ...v8CoveragesWithWebUrl,
@@ -25079,8 +25069,19 @@ const createRuntimeFromPlaywright = ({
25079
25069
  result.performance = performance;
25080
25070
  });
25081
25071
  }
25082
- const fileClientUrl = new URL(fileRelativeUrl, `${devServerOrigin}/`).href;
25083
-
25072
+ const fileUrl = new URL(fileRelativeUrl, rootDirectoryUrl).href;
25073
+ if (!urlIsInsideOf(fileUrl, sourceDirectoryUrl)) {
25074
+ throw new Error(`Cannot execute file that is outside source directory
25075
+ --- file ---
25076
+ ${fileUrl}
25077
+ --- source directory ---
25078
+ ${sourceDirectoryUrl}`);
25079
+ }
25080
+ const fileDevServerUrl = moveUrl({
25081
+ url: fileUrl,
25082
+ from: sourceDirectoryUrl,
25083
+ to: `${devServerOrigin}/`
25084
+ });
25084
25085
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
25085
25086
  const removeConsoleListener = registerEvent({
25086
25087
  object: page,
@@ -25163,7 +25164,7 @@ const createRuntimeFromPlaywright = ({
25163
25164
  },
25164
25165
  response: async cb => {
25165
25166
  try {
25166
- await page.goto(fileClientUrl, {
25167
+ await page.goto(fileDevServerUrl, {
25167
25168
  timeout: 0
25168
25169
  });
25169
25170
  const returnValue = await page.evaluate( /* eslint-disable no-undef */
@@ -26405,6 +26406,7 @@ const execute = async ({
26405
26406
  handleSIGINT = true,
26406
26407
  logLevel,
26407
26408
  rootDirectoryUrl,
26409
+ sourceDirectoryUrl = rootDirectoryUrl,
26408
26410
  devServerOrigin,
26409
26411
  fileRelativeUrl,
26410
26412
  allocatedMs,
@@ -26434,6 +26436,7 @@ const execute = async ({
26434
26436
  let resultTransformer = result => result;
26435
26437
  runtimeParams = {
26436
26438
  rootDirectoryUrl,
26439
+ sourceDirectoryUrl,
26437
26440
  devServerOrigin,
26438
26441
  fileRelativeUrl,
26439
26442
  ...runtimeParams
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "32.2.4",
3
+ "version": "33.0.0",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -69,17 +69,17 @@
69
69
  "@jsenv/abort": "4.2.4",
70
70
  "@jsenv/ast": "3.0.3",
71
71
  "@jsenv/babel-plugins": "1.1.5",
72
- "@jsenv/filesystem": "4.2.2",
72
+ "@jsenv/filesystem": "4.2.3",
73
73
  "@jsenv/importmap": "1.2.1",
74
74
  "@jsenv/integrity": "0.0.1",
75
75
  "@jsenv/log": "3.3.4",
76
76
  "@jsenv/node-esm-resolution": "1.0.1",
77
- "@jsenv/plugin-bundling": "2.1.2",
77
+ "@jsenv/plugin-bundling": "2.1.3",
78
78
  "@jsenv/server": "15.0.2",
79
- "@jsenv/sourcemap": "1.0.9",
79
+ "@jsenv/sourcemap": "1.0.10",
80
80
  "@jsenv/uneval": "1.6.0",
81
81
  "@jsenv/url-meta": "8.1.0",
82
- "@jsenv/urls": "1.2.8",
82
+ "@jsenv/urls": "2.0.0",
83
83
  "@jsenv/utils": "2.0.1",
84
84
  "@paralleldrive/cuid2": "2.2.0",
85
85
  "construct-style-sheets-polyfill": "3.1.0",
@@ -446,6 +446,5 @@ const inferParentFromRequest = (request, sourceDirectoryUrl) => {
446
446
  url: referer,
447
447
  from: `${request.origin}/`,
448
448
  to: sourceDirectoryUrl,
449
- preferAbsolute: true,
450
449
  })
451
450
  }
@@ -22,6 +22,7 @@ export const execute = async ({
22
22
  handleSIGINT = true,
23
23
  logLevel,
24
24
  rootDirectoryUrl,
25
+ sourceDirectoryUrl = rootDirectoryUrl,
25
26
  devServerOrigin,
26
27
 
27
28
  fileRelativeUrl,
@@ -59,6 +60,7 @@ export const execute = async ({
59
60
  let resultTransformer = (result) => result
60
61
  runtimeParams = {
61
62
  rootDirectoryUrl,
63
+ sourceDirectoryUrl,
62
64
  devServerOrigin,
63
65
  fileRelativeUrl,
64
66
  ...runtimeParams,
@@ -7,7 +7,7 @@ import {
7
7
  raceProcessTeardownEvents,
8
8
  raceCallbacks,
9
9
  } from "@jsenv/abort"
10
- import { moveUrl } from "@jsenv/urls"
10
+ import { moveUrl, urlIsInsideOf } from "@jsenv/urls"
11
11
  import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
12
12
 
13
13
  import { filterV8Coverage } from "@jsenv/core/src/test/coverage/v8_coverage.js"
@@ -33,6 +33,7 @@ export const createRuntimeFromPlaywright = ({
33
33
  rootDirectoryUrl,
34
34
  fileRelativeUrl,
35
35
  devServerOrigin,
36
+ sourceDirectoryUrl,
36
37
 
37
38
  // measurePerformance,
38
39
  collectPerformance,
@@ -145,8 +146,7 @@ export const createRuntimeFromPlaywright = ({
145
146
  const fsUrl = moveUrl({
146
147
  url: v8CoveragesWithWebUrl.url,
147
148
  from: `${devServerOrigin}/`,
148
- to: rootDirectoryUrl,
149
- preferAbsolute: true,
149
+ to: sourceDirectoryUrl,
150
150
  })
151
151
  return {
152
152
  ...v8CoveragesWithWebUrl,
@@ -218,8 +218,19 @@ export const createRuntimeFromPlaywright = ({
218
218
  })
219
219
  }
220
220
 
221
- const fileClientUrl = new URL(fileRelativeUrl, `${devServerOrigin}/`).href
222
-
221
+ const fileUrl = new URL(fileRelativeUrl, rootDirectoryUrl).href
222
+ if (!urlIsInsideOf(fileUrl, sourceDirectoryUrl)) {
223
+ throw new Error(`Cannot execute file that is outside source directory
224
+ --- file ---
225
+ ${fileUrl}
226
+ --- source directory ---
227
+ ${sourceDirectoryUrl}`)
228
+ }
229
+ const fileDevServerUrl = moveUrl({
230
+ url: fileUrl,
231
+ from: sourceDirectoryUrl,
232
+ to: `${devServerOrigin}/`,
233
+ })
223
234
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
224
235
  const removeConsoleListener = registerEvent({
225
236
  object: page,
@@ -301,7 +312,7 @@ export const createRuntimeFromPlaywright = ({
301
312
  },
302
313
  response: async (cb) => {
303
314
  try {
304
- await page.goto(fileClientUrl, { timeout: 0 })
315
+ await page.goto(fileDevServerUrl, { timeout: 0 })
305
316
  const returnValue = await page.evaluate(
306
317
  /* eslint-disable no-undef */
307
318
  /* istanbul ignore next */
@@ -961,6 +961,5 @@ const determineFileUrlForOutDirectory = ({ urlInfo, context }) => {
961
961
  url,
962
962
  from: context.rootDirectoryUrl,
963
963
  to: context.outDirectoryUrl,
964
- preferAbsolute: true,
965
964
  })
966
965
  }
@@ -13,11 +13,10 @@ import { reportToCoverage } from "./coverage/report_to_coverage.js"
13
13
  import { run } from "@jsenv/core/src/execute/run.js"
14
14
 
15
15
  import { ensureGlobalGc } from "./gc.js"
16
- import { generateExecutionSteps } from "./execution_steps.js"
17
16
  import { createExecutionLog, createSummaryLog } from "./logs_file_execution.js"
18
17
 
19
- export const executePlan = async (
20
- plan,
18
+ export const executeSteps = async (
19
+ executionSteps,
21
20
  {
22
21
  signal,
23
22
  handleSIGINT,
@@ -33,6 +32,7 @@ export const executePlan = async (
33
32
  completedExecutionLogAbbreviation,
34
33
  rootDirectoryUrl,
35
34
  devServerOrigin,
35
+ sourceDirectoryUrl,
36
36
 
37
37
  keepRunning,
38
38
  defaultMsAllocatedPerExecution,
@@ -120,6 +120,8 @@ export const executePlan = async (
120
120
  let runtimeParams = {
121
121
  rootDirectoryUrl,
122
122
  devServerOrigin,
123
+ sourceDirectoryUrl,
124
+
123
125
  coverageEnabled,
124
126
  coverageConfig,
125
127
  coverageMethodForBrowsers,
@@ -127,13 +129,6 @@ export const executePlan = async (
127
129
  stopAfterAllSignal,
128
130
  }
129
131
 
130
- logger.debug(`Generate executions`)
131
- const executionSteps = await getExecutionAsSteps({
132
- plan,
133
- multipleExecutionsOperation,
134
- rootDirectoryUrl,
135
- })
136
- logger.debug(`${executionSteps.length} executions planned`)
137
132
  if (completedExecutionLogMerging && !process.stdout.isTTY) {
138
133
  completedExecutionLogMerging = false
139
134
  logger.debug(
@@ -368,30 +363,6 @@ export const executePlan = async (
368
363
  }
369
364
  }
370
365
 
371
- const getExecutionAsSteps = async ({
372
- plan,
373
- multipleExecutionsOperation,
374
- rootDirectoryUrl,
375
- }) => {
376
- try {
377
- const executionSteps = await generateExecutionSteps(plan, {
378
- signal: multipleExecutionsOperation.signal,
379
- rootDirectoryUrl,
380
- })
381
- return executionSteps
382
- } catch (e) {
383
- if (Abort.isAbortError(e)) {
384
- return {
385
- aborted: true,
386
- planSummary: {},
387
- planReport: {},
388
- planCoverage: null,
389
- }
390
- }
391
- throw e
392
- }
393
- }
394
-
395
366
  const canOverwriteLogGetter = ({
396
367
  completedExecutionLogMerging,
397
368
  executionResult,
@@ -1,10 +1,6 @@
1
1
  import { existsSync } from "node:fs"
2
2
  import { URL_META } from "@jsenv/url-meta"
3
- import {
4
- urlToFileSystemPath,
5
- urlToRelativeUrl,
6
- urlIsInsideOf,
7
- } from "@jsenv/urls"
3
+ import { urlToFileSystemPath, urlToRelativeUrl } from "@jsenv/urls"
8
4
  import {
9
5
  ensureEmptyDirectory,
10
6
  assertAndNormalizeDirectoryUrl,
@@ -12,20 +8,20 @@ import {
12
8
  } from "@jsenv/filesystem"
13
9
  import { createLogger, createDetailedMessage } from "@jsenv/log"
14
10
 
15
- import { lookupPackageDirectory } from "../lookup_package_directory.js"
16
11
  import { pingServer } from "../ping_server.js"
17
12
  import { basicFetch } from "../basic_fetch.js"
18
13
  import { generateCoverageJsonFile } from "./coverage/coverage_reporter_json_file.js"
19
14
  import { generateCoverageHtmlDirectory } from "./coverage/coverage_reporter_html_directory.js"
20
15
  import { generateCoverageTextLog } from "./coverage/coverage_reporter_text_log.js"
21
- import { executePlan } from "./execute_plan.js"
16
+ import { executionStepsFromTestPlan } from "./execution_steps.js"
17
+ import { executeSteps } from "./execute_steps.js"
22
18
 
23
19
  /**
24
20
  * Execute a list of files and log how it goes.
25
21
  * @param {Object} testPlanParameters
26
- * @param {string|url} testPlanParameters.testDirectoryUrl Directory containing test files
22
+ * @param {string|url} testPlanParameters.rootDirectoryUrl Directory containing test files;
27
23
  * @param {string|url} [testPlanParameters.devServerOrigin=undefined] Jsenv dev server origin; required when executing test on browsers
28
- * @param {Object} testPlanParameters.testPlan Object associating patterns leading to files to runtimes where they should be executed
24
+ * @param {Object} testPlanParameters.testPlan Object associating files with runtimes where they will be executed
29
25
  * @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
30
26
  * @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
31
27
  * @param {number} [testPlanParameters.maxExecutionsInParallel=1] Maximum amount of execution in parallel
@@ -50,7 +46,7 @@ export const executeTestPlan = async ({
50
46
  logFileRelativeUrl = ".jsenv/test_plan_debug.txt",
51
47
  completedExecutionLogAbbreviation = false,
52
48
  completedExecutionLogMerging = false,
53
- testDirectoryUrl,
49
+ rootDirectoryUrl,
54
50
  devServerModuleUrl,
55
51
  devServerOrigin,
56
52
 
@@ -69,7 +65,16 @@ export const executeTestPlan = async ({
69
65
  gcBetweenExecutions = logMemoryHeapUsage,
70
66
 
71
67
  coverageEnabled = process.argv.includes("--coverage"),
72
- coverageConfig = { "./**/*": true },
68
+ coverageConfig = {
69
+ "**/*": true,
70
+ "**/.*": false,
71
+ "**/.*/": false,
72
+ "**/node_modules/": false,
73
+ "**/tests/": false,
74
+ "**/*.test.html": false,
75
+ "**/*.test.js": false,
76
+ "**/*.test.mjs": false,
77
+ },
73
78
  coverageIncludeMissing = true,
74
79
  coverageAndExecutionAllowed = false,
75
80
  coverageMethodForNodeJs = process.env.NODE_V8_COVERAGE
@@ -78,7 +83,6 @@ export const executeTestPlan = async ({
78
83
  coverageMethodForBrowsers = "playwright_api", // "istanbul" also accepted
79
84
  coverageV8ConflictWarning = true,
80
85
  coverageTempDirectoryUrl,
81
- coverageReportRootDirectoryUrl,
82
86
  // skip empty means empty files won't appear in the coverage reports (json and html)
83
87
  coverageReportSkipEmpty = false,
84
88
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
@@ -93,6 +97,7 @@ export const executeTestPlan = async ({
93
97
  let someNeedsServer = false
94
98
  let someNodeRuntime = false
95
99
  let stopDevServerNeeded = false
100
+ let sourceDirectoryUrl
96
101
  const runtimes = {}
97
102
  // param validation
98
103
  {
@@ -102,12 +107,12 @@ export const executeTestPlan = async ({
102
107
  `${unexpectedParamNames.join(",")}: there is no such param`,
103
108
  )
104
109
  }
105
- testDirectoryUrl = assertAndNormalizeDirectoryUrl(
106
- testDirectoryUrl,
107
- "testDirectoryUrl",
110
+ rootDirectoryUrl = assertAndNormalizeDirectoryUrl(
111
+ rootDirectoryUrl,
112
+ "rootDirectoryUrl",
108
113
  )
109
- if (!existsSync(new URL(testDirectoryUrl))) {
110
- throw new Error(`ENOENT on testDirectoryUrl at ${testDirectoryUrl}`)
114
+ if (!existsSync(new URL(rootDirectoryUrl))) {
115
+ throw new Error(`ENOENT on rootDirectoryUrl at ${rootDirectoryUrl}`)
111
116
  }
112
117
  if (typeof testPlan !== "object") {
113
118
  throw new Error(`testPlan must be an object, got ${testPlan}`)
@@ -164,18 +169,12 @@ export const executeTestPlan = async ({
164
169
  }
165
170
  stopDevServerNeeded = true
166
171
  }
167
- const { sourceDirectoryUrl } = await basicFetch(
172
+
173
+ const devServerParams = await basicFetch(
168
174
  `${devServerOrigin}/__server_params__.json`,
169
175
  { rejectUnauthorized: false },
170
176
  )
171
- if (
172
- testDirectoryUrl !== sourceDirectoryUrl &&
173
- !urlIsInsideOf(testDirectoryUrl, sourceDirectoryUrl)
174
- ) {
175
- throw new Error(
176
- `testDirectoryUrl must be inside sourceDirectoryUrl when running tests on browser(s)`,
177
- )
178
- }
177
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl
179
178
  }
180
179
 
181
180
  if (coverageEnabled) {
@@ -214,20 +213,9 @@ export const executeTestPlan = async ({
214
213
  )
215
214
  }
216
215
  }
217
- if (coverageReportRootDirectoryUrl === undefined) {
218
- coverageReportRootDirectoryUrl =
219
- lookupPackageDirectory(testDirectoryUrl)
220
- } else {
221
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(
222
- coverageReportRootDirectoryUrl,
223
- "coverageReportRootDirectoryUrl",
224
- )
225
- }
216
+
226
217
  if (coverageTempDirectoryUrl === undefined) {
227
- coverageTempDirectoryUrl = new URL(
228
- "./.coverage/tmp/",
229
- coverageReportRootDirectoryUrl,
230
- )
218
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl)
231
219
  } else {
232
220
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(
233
221
  coverageTempDirectoryUrl,
@@ -238,7 +226,7 @@ export const executeTestPlan = async ({
238
226
  if (coverageReportJsonFileUrl === undefined) {
239
227
  coverageReportJsonFileUrl = new URL(
240
228
  "./.coverage/coverage.json",
241
- coverageReportRootDirectoryUrl,
229
+ rootDirectoryUrl,
242
230
  )
243
231
  } else {
244
232
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(
@@ -251,7 +239,7 @@ export const executeTestPlan = async ({
251
239
  if (coverageReportHtmlDirectoryUrl === undefined) {
252
240
  coverageReportHtmlDirectoryUrl = new URL(
253
241
  "./.coverage/",
254
- coverageReportRootDirectoryUrl,
242
+ rootDirectoryUrl,
255
243
  )
256
244
  } else {
257
245
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(
@@ -303,8 +291,15 @@ export const executeTestPlan = async ({
303
291
  }
304
292
 
305
293
  testPlan = { ...testPlan, "**/.jsenv/": null }
294
+ logger.debug(`Generate executions`)
295
+ const executionSteps = await executionStepsFromTestPlan({
296
+ signal,
297
+ testPlan,
298
+ rootDirectoryUrl,
299
+ })
300
+ logger.debug(`${executionSteps.length} executions planned`)
306
301
 
307
- const result = await executePlan(testPlan, {
302
+ const result = await executeSteps(executionSteps, {
308
303
  signal,
309
304
  handleSIGINT,
310
305
  logger,
@@ -317,8 +312,9 @@ export const executeTestPlan = async ({
317
312
  logFileRelativeUrl,
318
313
  completedExecutionLogMerging,
319
314
  completedExecutionLogAbbreviation,
320
- rootDirectoryUrl: testDirectoryUrl,
315
+ rootDirectoryUrl,
321
316
  devServerOrigin,
317
+ sourceDirectoryUrl,
322
318
 
323
319
  maxExecutionsInParallel,
324
320
  defaultMsAllocatedPerExecution,
@@ -367,10 +363,10 @@ export const executeTestPlan = async ({
367
363
  )
368
364
  promises.push(
369
365
  generateCoverageHtmlDirectory(planCoverage, {
370
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
366
+ rootDirectoryUrl,
371
367
  coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(
372
368
  coverageReportHtmlDirectoryUrl,
373
- coverageReportRootDirectoryUrl,
369
+ rootDirectoryUrl,
374
370
  ),
375
371
  coverageReportSkipEmpty,
376
372
  coverageReportSkipFull,
@@ -1,25 +1,39 @@
1
+ import { Abort } from "@jsenv/abort"
1
2
  import { collectFiles } from "@jsenv/filesystem"
2
3
  import { createDetailedMessage } from "@jsenv/log"
3
4
 
4
- export const generateExecutionSteps = async (
5
- plan,
6
- { signal, rootDirectoryUrl },
7
- ) => {
8
- const fileResultArray = await collectFiles({
9
- signal,
10
- directoryUrl: rootDirectoryUrl,
11
- associations: { filePlan: plan },
12
- predicate: ({ filePlan }) => filePlan,
13
- })
14
- const executionSteps = []
15
- fileResultArray.forEach(({ relativeUrl, meta }) => {
16
- const fileExecutionSteps = generateFileExecutionSteps({
17
- fileRelativeUrl: relativeUrl,
18
- filePlan: meta.filePlan,
5
+ export const executionStepsFromTestPlan = async ({
6
+ signal,
7
+ rootDirectoryUrl,
8
+ testPlan,
9
+ }) => {
10
+ try {
11
+ const fileResultArray = await collectFiles({
12
+ signal,
13
+ directoryUrl: rootDirectoryUrl,
14
+ associations: { testPlan },
15
+ predicate: ({ testPlan }) => testPlan,
19
16
  })
20
- executionSteps.push(...fileExecutionSteps)
21
- })
22
- return executionSteps
17
+ const executionSteps = []
18
+ fileResultArray.forEach(({ relativeUrl, meta }) => {
19
+ const fileExecutionSteps = generateFileExecutionSteps({
20
+ fileRelativeUrl: relativeUrl,
21
+ filePlan: meta.testPlan,
22
+ })
23
+ executionSteps.push(...fileExecutionSteps)
24
+ })
25
+ return executionSteps
26
+ } catch (e) {
27
+ if (Abort.isAbortError(e)) {
28
+ return {
29
+ aborted: true,
30
+ planSummary: {},
31
+ planReport: {},
32
+ planCoverage: null,
33
+ }
34
+ }
35
+ throw e
36
+ }
23
37
  }
24
38
 
25
39
  export const generateFileExecutionSteps = ({ fileRelativeUrl, filePlan }) => {
@@ -15,7 +15,7 @@ export const watchSourceFiles = (
15
15
  "**/*": true, // by default watch everything inside the source directory
16
16
  // line below is commented until @jsenv/url-meta fixes the fact that is matches
17
17
  // any file with an extension
18
- // "**/.*": false, // file starting with a dot -> do not watch
18
+ "**/.*": false, // file starting with a dot -> do not watch
19
19
  "**/.*/": false, // directory starting with a dot -> do not watch
20
20
  "**/node_modules/": false, // node_modules directory -> do not watch
21
21
  ...sourceFileConfig,