@jsenv/core 32.2.4 → 33.0.1

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,11 @@ const executeTestPlan = async ({
24631
24615
  gcBetweenExecutions = logMemoryHeapUsage,
24632
24616
  coverageEnabled = process.argv.includes("--coverage"),
24633
24617
  coverageConfig = {
24634
- "./**/*": true
24618
+ "./**/src/**": true,
24619
+ "./**/tests/": false,
24620
+ "./**/*.test.html": false,
24621
+ "./**/*.test.js": false,
24622
+ "./**/*.test.mjs": false
24635
24623
  },
24636
24624
  coverageIncludeMissing = true,
24637
24625
  coverageAndExecutionAllowed = false,
@@ -24640,7 +24628,6 @@ const executeTestPlan = async ({
24640
24628
  // "istanbul" also accepted
24641
24629
  coverageV8ConflictWarning = true,
24642
24630
  coverageTempDirectoryUrl,
24643
- coverageReportRootDirectoryUrl,
24644
24631
  // skip empty means empty files won't appear in the coverage reports (json and html)
24645
24632
  coverageReportSkipEmpty = false,
24646
24633
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
@@ -24655,6 +24642,7 @@ const executeTestPlan = async ({
24655
24642
  let someNeedsServer = false;
24656
24643
  let someNodeRuntime = false;
24657
24644
  let stopDevServerNeeded = false;
24645
+ let sourceDirectoryUrl;
24658
24646
  const runtimes = {};
24659
24647
  // param validation
24660
24648
  {
@@ -24662,9 +24650,9 @@ const executeTestPlan = async ({
24662
24650
  if (unexpectedParamNames.length > 0) {
24663
24651
  throw new TypeError(`${unexpectedParamNames.join(",")}: there is no such param`);
24664
24652
  }
24665
- testDirectoryUrl = assertAndNormalizeDirectoryUrl(testDirectoryUrl, "testDirectoryUrl");
24666
- if (!existsSync(new URL(testDirectoryUrl))) {
24667
- throw new Error(`ENOENT on testDirectoryUrl at ${testDirectoryUrl}`);
24653
+ rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl, "rootDirectoryUrl");
24654
+ if (!existsSync(new URL(rootDirectoryUrl))) {
24655
+ throw new Error(`ENOENT on rootDirectoryUrl at ${rootDirectoryUrl}`);
24668
24656
  }
24669
24657
  if (typeof testPlan !== "object") {
24670
24658
  throw new Error(`testPlan must be an object, got ${testPlan}`);
@@ -24713,14 +24701,10 @@ const executeTestPlan = async ({
24713
24701
  }
24714
24702
  stopDevServerNeeded = true;
24715
24703
  }
24716
- const {
24717
- sourceDirectoryUrl
24718
- } = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
24704
+ const devServerParams = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
24719
24705
  rejectUnauthorized: false
24720
24706
  });
24721
- if (testDirectoryUrl !== sourceDirectoryUrl && !urlIsInsideOf(testDirectoryUrl, sourceDirectoryUrl)) {
24722
- throw new Error(`testDirectoryUrl must be inside sourceDirectoryUrl when running tests on browser(s)`);
24723
- }
24707
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl;
24724
24708
  }
24725
24709
  if (coverageEnabled) {
24726
24710
  if (typeof coverageConfig !== "object") {
@@ -24749,26 +24733,21 @@ const executeTestPlan = async ({
24749
24733
  }));
24750
24734
  }
24751
24735
  }
24752
- if (coverageReportRootDirectoryUrl === undefined) {
24753
- coverageReportRootDirectoryUrl = lookupPackageDirectory(testDirectoryUrl);
24754
- } else {
24755
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportRootDirectoryUrl, "coverageReportRootDirectoryUrl");
24756
- }
24757
24736
  if (coverageTempDirectoryUrl === undefined) {
24758
- coverageTempDirectoryUrl = new URL("./.coverage/tmp/", coverageReportRootDirectoryUrl);
24737
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl);
24759
24738
  } else {
24760
24739
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageTempDirectoryUrl, "coverageTempDirectoryUrl");
24761
24740
  }
24762
24741
  if (coverageReportJson) {
24763
24742
  if (coverageReportJsonFileUrl === undefined) {
24764
- coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", coverageReportRootDirectoryUrl);
24743
+ coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", rootDirectoryUrl);
24765
24744
  } else {
24766
24745
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(coverageReportJsonFileUrl, "coverageReportJsonFileUrl");
24767
24746
  }
24768
24747
  }
24769
24748
  if (coverageReportHtml) {
24770
24749
  if (coverageReportHtmlDirectoryUrl === undefined) {
24771
- coverageReportHtmlDirectoryUrl = new URL("./.coverage/", coverageReportRootDirectoryUrl);
24750
+ coverageReportHtmlDirectoryUrl = new URL("./.coverage/", rootDirectoryUrl);
24772
24751
  } else {
24773
24752
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportHtmlDirectoryUrl, "coverageReportHtmlDirectoryUrl");
24774
24753
  }
@@ -24806,7 +24785,14 @@ const executeTestPlan = async ({
24806
24785
  ...testPlan,
24807
24786
  "**/.jsenv/": null
24808
24787
  };
24809
- const result = await executePlan(testPlan, {
24788
+ logger.debug(`Generate executions`);
24789
+ const executionSteps = await executionStepsFromTestPlan({
24790
+ signal,
24791
+ testPlan,
24792
+ rootDirectoryUrl
24793
+ });
24794
+ logger.debug(`${executionSteps.length} executions planned`);
24795
+ const result = await executeSteps(executionSteps, {
24810
24796
  signal,
24811
24797
  handleSIGINT,
24812
24798
  logger,
@@ -24819,8 +24805,9 @@ const executeTestPlan = async ({
24819
24805
  logFileRelativeUrl,
24820
24806
  completedExecutionLogMerging,
24821
24807
  completedExecutionLogAbbreviation,
24822
- rootDirectoryUrl: testDirectoryUrl,
24808
+ rootDirectoryUrl,
24823
24809
  devServerOrigin,
24810
+ sourceDirectoryUrl,
24824
24811
  maxExecutionsInParallel,
24825
24812
  defaultMsAllocatedPerExecution,
24826
24813
  failFast,
@@ -24861,8 +24848,8 @@ const executeTestPlan = async ({
24861
24848
  const htmlCoverageDirectoryIndexFileUrl = `${coverageReportHtmlDirectoryUrl}index.html`;
24862
24849
  logger.info(`-> ${urlToFileSystemPath(htmlCoverageDirectoryIndexFileUrl)}`);
24863
24850
  promises.push(generateCoverageHtmlDirectory(planCoverage, {
24864
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
24865
- coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, coverageReportRootDirectoryUrl),
24851
+ rootDirectoryUrl,
24852
+ coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, rootDirectoryUrl),
24866
24853
  coverageReportSkipEmpty,
24867
24854
  coverageReportSkipFull
24868
24855
  }));
@@ -24910,6 +24897,7 @@ const createRuntimeFromPlaywright = ({
24910
24897
  rootDirectoryUrl,
24911
24898
  fileRelativeUrl,
24912
24899
  devServerOrigin,
24900
+ sourceDirectoryUrl,
24913
24901
  // measurePerformance,
24914
24902
  collectPerformance,
24915
24903
  coverageEnabled = false,
@@ -25016,8 +25004,7 @@ const createRuntimeFromPlaywright = ({
25016
25004
  const fsUrl = moveUrl({
25017
25005
  url: v8CoveragesWithWebUrl.url,
25018
25006
  from: `${devServerOrigin}/`,
25019
- to: rootDirectoryUrl,
25020
- preferAbsolute: true
25007
+ to: sourceDirectoryUrl
25021
25008
  });
25022
25009
  return {
25023
25010
  ...v8CoveragesWithWebUrl,
@@ -25079,8 +25066,19 @@ const createRuntimeFromPlaywright = ({
25079
25066
  result.performance = performance;
25080
25067
  });
25081
25068
  }
25082
- const fileClientUrl = new URL(fileRelativeUrl, `${devServerOrigin}/`).href;
25083
-
25069
+ const fileUrl = new URL(fileRelativeUrl, rootDirectoryUrl).href;
25070
+ if (!urlIsInsideOf(fileUrl, sourceDirectoryUrl)) {
25071
+ throw new Error(`Cannot execute file that is outside source directory
25072
+ --- file ---
25073
+ ${fileUrl}
25074
+ --- source directory ---
25075
+ ${sourceDirectoryUrl}`);
25076
+ }
25077
+ const fileDevServerUrl = moveUrl({
25078
+ url: fileUrl,
25079
+ from: sourceDirectoryUrl,
25080
+ to: `${devServerOrigin}/`
25081
+ });
25084
25082
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
25085
25083
  const removeConsoleListener = registerEvent({
25086
25084
  object: page,
@@ -25163,7 +25161,7 @@ const createRuntimeFromPlaywright = ({
25163
25161
  },
25164
25162
  response: async cb => {
25165
25163
  try {
25166
- await page.goto(fileClientUrl, {
25164
+ await page.goto(fileDevServerUrl, {
25167
25165
  timeout: 0
25168
25166
  });
25169
25167
  const returnValue = await page.evaluate( /* eslint-disable no-undef */
@@ -26405,6 +26403,7 @@ const execute = async ({
26405
26403
  handleSIGINT = true,
26406
26404
  logLevel,
26407
26405
  rootDirectoryUrl,
26406
+ sourceDirectoryUrl = rootDirectoryUrl,
26408
26407
  devServerOrigin,
26409
26408
  fileRelativeUrl,
26410
26409
  allocatedMs,
@@ -26434,6 +26433,7 @@ const execute = async ({
26434
26433
  let resultTransformer = result => result;
26435
26434
  runtimeParams = {
26436
26435
  rootDirectoryUrl,
26436
+ sourceDirectoryUrl,
26437
26437
  devServerOrigin,
26438
26438
  fileRelativeUrl,
26439
26439
  ...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.1",
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,13 @@ export const executeTestPlan = async ({
69
65
  gcBetweenExecutions = logMemoryHeapUsage,
70
66
 
71
67
  coverageEnabled = process.argv.includes("--coverage"),
72
- coverageConfig = { "./**/*": true },
68
+ coverageConfig = {
69
+ "./**/src/**": true,
70
+ "./**/tests/": false,
71
+ "./**/*.test.html": false,
72
+ "./**/*.test.js": false,
73
+ "./**/*.test.mjs": false,
74
+ },
73
75
  coverageIncludeMissing = true,
74
76
  coverageAndExecutionAllowed = false,
75
77
  coverageMethodForNodeJs = process.env.NODE_V8_COVERAGE
@@ -78,7 +80,6 @@ export const executeTestPlan = async ({
78
80
  coverageMethodForBrowsers = "playwright_api", // "istanbul" also accepted
79
81
  coverageV8ConflictWarning = true,
80
82
  coverageTempDirectoryUrl,
81
- coverageReportRootDirectoryUrl,
82
83
  // skip empty means empty files won't appear in the coverage reports (json and html)
83
84
  coverageReportSkipEmpty = false,
84
85
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
@@ -93,6 +94,7 @@ export const executeTestPlan = async ({
93
94
  let someNeedsServer = false
94
95
  let someNodeRuntime = false
95
96
  let stopDevServerNeeded = false
97
+ let sourceDirectoryUrl
96
98
  const runtimes = {}
97
99
  // param validation
98
100
  {
@@ -102,12 +104,12 @@ export const executeTestPlan = async ({
102
104
  `${unexpectedParamNames.join(",")}: there is no such param`,
103
105
  )
104
106
  }
105
- testDirectoryUrl = assertAndNormalizeDirectoryUrl(
106
- testDirectoryUrl,
107
- "testDirectoryUrl",
107
+ rootDirectoryUrl = assertAndNormalizeDirectoryUrl(
108
+ rootDirectoryUrl,
109
+ "rootDirectoryUrl",
108
110
  )
109
- if (!existsSync(new URL(testDirectoryUrl))) {
110
- throw new Error(`ENOENT on testDirectoryUrl at ${testDirectoryUrl}`)
111
+ if (!existsSync(new URL(rootDirectoryUrl))) {
112
+ throw new Error(`ENOENT on rootDirectoryUrl at ${rootDirectoryUrl}`)
111
113
  }
112
114
  if (typeof testPlan !== "object") {
113
115
  throw new Error(`testPlan must be an object, got ${testPlan}`)
@@ -164,18 +166,12 @@ export const executeTestPlan = async ({
164
166
  }
165
167
  stopDevServerNeeded = true
166
168
  }
167
- const { sourceDirectoryUrl } = await basicFetch(
169
+
170
+ const devServerParams = await basicFetch(
168
171
  `${devServerOrigin}/__server_params__.json`,
169
172
  { rejectUnauthorized: false },
170
173
  )
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
- }
174
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl
179
175
  }
180
176
 
181
177
  if (coverageEnabled) {
@@ -214,20 +210,9 @@ export const executeTestPlan = async ({
214
210
  )
215
211
  }
216
212
  }
217
- if (coverageReportRootDirectoryUrl === undefined) {
218
- coverageReportRootDirectoryUrl =
219
- lookupPackageDirectory(testDirectoryUrl)
220
- } else {
221
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(
222
- coverageReportRootDirectoryUrl,
223
- "coverageReportRootDirectoryUrl",
224
- )
225
- }
213
+
226
214
  if (coverageTempDirectoryUrl === undefined) {
227
- coverageTempDirectoryUrl = new URL(
228
- "./.coverage/tmp/",
229
- coverageReportRootDirectoryUrl,
230
- )
215
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl)
231
216
  } else {
232
217
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(
233
218
  coverageTempDirectoryUrl,
@@ -238,7 +223,7 @@ export const executeTestPlan = async ({
238
223
  if (coverageReportJsonFileUrl === undefined) {
239
224
  coverageReportJsonFileUrl = new URL(
240
225
  "./.coverage/coverage.json",
241
- coverageReportRootDirectoryUrl,
226
+ rootDirectoryUrl,
242
227
  )
243
228
  } else {
244
229
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(
@@ -251,7 +236,7 @@ export const executeTestPlan = async ({
251
236
  if (coverageReportHtmlDirectoryUrl === undefined) {
252
237
  coverageReportHtmlDirectoryUrl = new URL(
253
238
  "./.coverage/",
254
- coverageReportRootDirectoryUrl,
239
+ rootDirectoryUrl,
255
240
  )
256
241
  } else {
257
242
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(
@@ -303,8 +288,15 @@ export const executeTestPlan = async ({
303
288
  }
304
289
 
305
290
  testPlan = { ...testPlan, "**/.jsenv/": null }
291
+ logger.debug(`Generate executions`)
292
+ const executionSteps = await executionStepsFromTestPlan({
293
+ signal,
294
+ testPlan,
295
+ rootDirectoryUrl,
296
+ })
297
+ logger.debug(`${executionSteps.length} executions planned`)
306
298
 
307
- const result = await executePlan(testPlan, {
299
+ const result = await executeSteps(executionSteps, {
308
300
  signal,
309
301
  handleSIGINT,
310
302
  logger,
@@ -317,8 +309,9 @@ export const executeTestPlan = async ({
317
309
  logFileRelativeUrl,
318
310
  completedExecutionLogMerging,
319
311
  completedExecutionLogAbbreviation,
320
- rootDirectoryUrl: testDirectoryUrl,
312
+ rootDirectoryUrl,
321
313
  devServerOrigin,
314
+ sourceDirectoryUrl,
322
315
 
323
316
  maxExecutionsInParallel,
324
317
  defaultMsAllocatedPerExecution,
@@ -367,10 +360,10 @@ export const executeTestPlan = async ({
367
360
  )
368
361
  promises.push(
369
362
  generateCoverageHtmlDirectory(planCoverage, {
370
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
363
+ rootDirectoryUrl,
371
364
  coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(
372
365
  coverageReportHtmlDirectoryUrl,
373
- coverageReportRootDirectoryUrl,
366
+ rootDirectoryUrl,
374
367
  ),
375
368
  coverageReportSkipEmpty,
376
369
  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,