@jsenv/core 32.2.3 → 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/README.md CHANGED
@@ -7,7 +7,7 @@ It has naturally evolved to cover the core needs of a JavaScript project: develo
7
7
  - :sparkles: Same tooling for dev, tests and build.
8
8
  - :exploding_head: Can execute tests on Chrome, Firefox, Safari and Node.js.
9
9
 
10
- [Documentation](https://github.com/jsenv/jsenv-core/wiki)
10
+ [Documentation](https://github.com/jsenv/jsenv-core/wiki/A\)-Getting-started)
11
11
 
12
12
  # Installation
13
13
 
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
 
@@ -23018,6 +23017,7 @@ const pingServer = async url => {
23018
23017
  };
23019
23018
 
23020
23019
  const basicFetch = async (url, {
23020
+ rejectUnauthorized = true,
23021
23021
  method = "GET",
23022
23022
  headers = {}
23023
23023
  } = {}) => {
@@ -23033,6 +23033,7 @@ const basicFetch = async (url, {
23033
23033
  const urlObject = new URL(url);
23034
23034
  return new Promise((resolve, reject) => {
23035
23035
  const req = request({
23036
+ rejectUnauthorized,
23036
23037
  hostname: urlObject.hostname,
23037
23038
  port: urlObject.port,
23038
23039
  path: urlObject.pathname,
@@ -23122,6 +23123,72 @@ const generateCoverageTextLog = (coverage, {
23122
23123
  report.execute(context);
23123
23124
  };
23124
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
+
23125
23192
  const readNodeV8CoverageDirectory = async ({
23126
23193
  logger,
23127
23194
  signal,
@@ -23779,59 +23846,6 @@ const ensureGlobalGc = () => {
23779
23846
  }
23780
23847
  };
23781
23848
 
23782
- const generateExecutionSteps = async (plan, {
23783
- signal,
23784
- rootDirectoryUrl
23785
- }) => {
23786
- const fileResultArray = await collectFiles({
23787
- signal,
23788
- directoryUrl: rootDirectoryUrl,
23789
- associations: {
23790
- filePlan: plan
23791
- },
23792
- predicate: ({
23793
- filePlan
23794
- }) => filePlan
23795
- });
23796
- const executionSteps = [];
23797
- fileResultArray.forEach(({
23798
- relativeUrl,
23799
- meta
23800
- }) => {
23801
- const fileExecutionSteps = generateFileExecutionSteps({
23802
- fileRelativeUrl: relativeUrl,
23803
- filePlan: meta.filePlan
23804
- });
23805
- executionSteps.push(...fileExecutionSteps);
23806
- });
23807
- return executionSteps;
23808
- };
23809
- const generateFileExecutionSteps = ({
23810
- fileRelativeUrl,
23811
- filePlan
23812
- }) => {
23813
- const fileExecutionSteps = [];
23814
- Object.keys(filePlan).forEach(executionName => {
23815
- const stepConfig = filePlan[executionName];
23816
- if (stepConfig === null || stepConfig === undefined) {
23817
- return;
23818
- }
23819
- if (typeof stepConfig !== "object") {
23820
- throw new TypeError(createDetailedMessage$1(`found unexpected value in plan, they must be object`, {
23821
- ["file relative path"]: fileRelativeUrl,
23822
- ["execution name"]: executionName,
23823
- ["value"]: stepConfig
23824
- }));
23825
- }
23826
- fileExecutionSteps.push({
23827
- executionName,
23828
- fileRelativeUrl,
23829
- ...stepConfig
23830
- });
23831
- });
23832
- return fileExecutionSteps;
23833
- };
23834
-
23835
23849
  const EXECUTION_COLORS = {
23836
23850
  executing: ANSI.BLUE,
23837
23851
  aborted: ANSI.MAGENTA,
@@ -24172,7 +24186,7 @@ ${key}: ${details[key]}`;
24172
24186
  return message;
24173
24187
  };
24174
24188
 
24175
- const executePlan = async (plan, {
24189
+ const executeSteps = async (executionSteps, {
24176
24190
  signal,
24177
24191
  handleSIGINT,
24178
24192
  logger,
@@ -24187,6 +24201,7 @@ const executePlan = async (plan, {
24187
24201
  completedExecutionLogAbbreviation,
24188
24202
  rootDirectoryUrl,
24189
24203
  devServerOrigin,
24204
+ sourceDirectoryUrl,
24190
24205
  keepRunning,
24191
24206
  defaultMsAllocatedPerExecution,
24192
24207
  maxExecutionsInParallel,
@@ -24266,19 +24281,13 @@ const executePlan = async (plan, {
24266
24281
  let runtimeParams = {
24267
24282
  rootDirectoryUrl,
24268
24283
  devServerOrigin,
24284
+ sourceDirectoryUrl,
24269
24285
  coverageEnabled,
24270
24286
  coverageConfig,
24271
24287
  coverageMethodForBrowsers,
24272
24288
  coverageMethodForNodeJs,
24273
24289
  stopAfterAllSignal
24274
24290
  };
24275
- logger.debug(`Generate executions`);
24276
- const executionSteps = await getExecutionAsSteps({
24277
- plan,
24278
- multipleExecutionsOperation,
24279
- rootDirectoryUrl
24280
- });
24281
- logger.debug(`${executionSteps.length} executions planned`);
24282
24291
  if (completedExecutionLogMerging && !process.stdout.isTTY) {
24283
24292
  completedExecutionLogMerging = false;
24284
24293
  logger.debug(`Force completedExecutionLogMerging to false because process.stdout.isTTY is false`);
@@ -24495,29 +24504,6 @@ const executePlan = async (plan, {
24495
24504
  await multipleExecutionsOperation.end();
24496
24505
  }
24497
24506
  };
24498
- const getExecutionAsSteps = async ({
24499
- plan,
24500
- multipleExecutionsOperation,
24501
- rootDirectoryUrl
24502
- }) => {
24503
- try {
24504
- const executionSteps = await generateExecutionSteps(plan, {
24505
- signal: multipleExecutionsOperation.signal,
24506
- rootDirectoryUrl
24507
- });
24508
- return executionSteps;
24509
- } catch (e) {
24510
- if (Abort.isAbortError(e)) {
24511
- return {
24512
- aborted: true,
24513
- planSummary: {},
24514
- planReport: {},
24515
- planCoverage: null
24516
- };
24517
- }
24518
- throw e;
24519
- }
24520
- };
24521
24507
  const canOverwriteLogGetter = ({
24522
24508
  completedExecutionLogMerging,
24523
24509
  executionResult
@@ -24584,9 +24570,9 @@ const executeInParallel = async ({
24584
24570
  /**
24585
24571
  * Execute a list of files and log how it goes.
24586
24572
  * @param {Object} testPlanParameters
24587
- * @param {string|url} testPlanParameters.testDirectoryUrl Directory containing test files
24573
+ * @param {string|url} testPlanParameters.rootDirectoryUrl Directory containing test files;
24588
24574
  * @param {string|url} [testPlanParameters.devServerOrigin=undefined] Jsenv dev server origin; required when executing test on browsers
24589
- * @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
24590
24576
  * @param {boolean} [testPlanParameters.completedExecutionLogAbbreviation=false] Abbreviate completed execution information to shorten terminal output
24591
24577
  * @param {boolean} [testPlanParameters.completedExecutionLogMerging=false] Merge completed execution logs to shorten terminal output
24592
24578
  * @param {number} [testPlanParameters.maxExecutionsInParallel=1] Maximum amount of execution in parallel
@@ -24611,7 +24597,7 @@ const executeTestPlan = async ({
24611
24597
  logFileRelativeUrl = ".jsenv/test_plan_debug.txt",
24612
24598
  completedExecutionLogAbbreviation = false,
24613
24599
  completedExecutionLogMerging = false,
24614
- testDirectoryUrl,
24600
+ rootDirectoryUrl,
24615
24601
  devServerModuleUrl,
24616
24602
  devServerOrigin,
24617
24603
  testPlan,
@@ -24629,7 +24615,14 @@ const executeTestPlan = async ({
24629
24615
  gcBetweenExecutions = logMemoryHeapUsage,
24630
24616
  coverageEnabled = process.argv.includes("--coverage"),
24631
24617
  coverageConfig = {
24632
- "./**/*": 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
24633
24626
  },
24634
24627
  coverageIncludeMissing = true,
24635
24628
  coverageAndExecutionAllowed = false,
@@ -24638,7 +24631,6 @@ const executeTestPlan = async ({
24638
24631
  // "istanbul" also accepted
24639
24632
  coverageV8ConflictWarning = true,
24640
24633
  coverageTempDirectoryUrl,
24641
- coverageReportRootDirectoryUrl,
24642
24634
  // skip empty means empty files won't appear in the coverage reports (json and html)
24643
24635
  coverageReportSkipEmpty = false,
24644
24636
  // skip full means file with 100% coverage won't appear in coverage reports (json and html)
@@ -24653,6 +24645,7 @@ const executeTestPlan = async ({
24653
24645
  let someNeedsServer = false;
24654
24646
  let someNodeRuntime = false;
24655
24647
  let stopDevServerNeeded = false;
24648
+ let sourceDirectoryUrl;
24656
24649
  const runtimes = {};
24657
24650
  // param validation
24658
24651
  {
@@ -24660,9 +24653,9 @@ const executeTestPlan = async ({
24660
24653
  if (unexpectedParamNames.length > 0) {
24661
24654
  throw new TypeError(`${unexpectedParamNames.join(",")}: there is no such param`);
24662
24655
  }
24663
- testDirectoryUrl = assertAndNormalizeDirectoryUrl(testDirectoryUrl, "testDirectoryUrl");
24664
- if (!existsSync(new URL(testDirectoryUrl))) {
24665
- 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}`);
24666
24659
  }
24667
24660
  if (typeof testPlan !== "object") {
24668
24661
  throw new Error(`testPlan must be an object, got ${testPlan}`);
@@ -24711,12 +24704,10 @@ const executeTestPlan = async ({
24711
24704
  }
24712
24705
  stopDevServerNeeded = true;
24713
24706
  }
24714
- const {
24715
- sourceDirectoryUrl
24716
- } = await basicFetch(`${devServerOrigin}/__server_params__.json`);
24717
- if (testDirectoryUrl !== sourceDirectoryUrl && !urlIsInsideOf(testDirectoryUrl, sourceDirectoryUrl)) {
24718
- throw new Error(`testDirectoryUrl must be inside sourceDirectoryUrl when running tests on browser(s)`);
24719
- }
24707
+ const devServerParams = await basicFetch(`${devServerOrigin}/__server_params__.json`, {
24708
+ rejectUnauthorized: false
24709
+ });
24710
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl;
24720
24711
  }
24721
24712
  if (coverageEnabled) {
24722
24713
  if (typeof coverageConfig !== "object") {
@@ -24745,26 +24736,21 @@ const executeTestPlan = async ({
24745
24736
  }));
24746
24737
  }
24747
24738
  }
24748
- if (coverageReportRootDirectoryUrl === undefined) {
24749
- coverageReportRootDirectoryUrl = lookupPackageDirectory(testDirectoryUrl);
24750
- } else {
24751
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportRootDirectoryUrl, "coverageReportRootDirectoryUrl");
24752
- }
24753
24739
  if (coverageTempDirectoryUrl === undefined) {
24754
- coverageTempDirectoryUrl = new URL("./.coverage/tmp/", coverageReportRootDirectoryUrl);
24740
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl);
24755
24741
  } else {
24756
24742
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageTempDirectoryUrl, "coverageTempDirectoryUrl");
24757
24743
  }
24758
24744
  if (coverageReportJson) {
24759
24745
  if (coverageReportJsonFileUrl === undefined) {
24760
- coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", coverageReportRootDirectoryUrl);
24746
+ coverageReportJsonFileUrl = new URL("./.coverage/coverage.json", rootDirectoryUrl);
24761
24747
  } else {
24762
24748
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(coverageReportJsonFileUrl, "coverageReportJsonFileUrl");
24763
24749
  }
24764
24750
  }
24765
24751
  if (coverageReportHtml) {
24766
24752
  if (coverageReportHtmlDirectoryUrl === undefined) {
24767
- coverageReportHtmlDirectoryUrl = new URL("./.coverage/", coverageReportRootDirectoryUrl);
24753
+ coverageReportHtmlDirectoryUrl = new URL("./.coverage/", rootDirectoryUrl);
24768
24754
  } else {
24769
24755
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(coverageReportHtmlDirectoryUrl, "coverageReportHtmlDirectoryUrl");
24770
24756
  }
@@ -24802,7 +24788,14 @@ const executeTestPlan = async ({
24802
24788
  ...testPlan,
24803
24789
  "**/.jsenv/": null
24804
24790
  };
24805
- 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, {
24806
24799
  signal,
24807
24800
  handleSIGINT,
24808
24801
  logger,
@@ -24815,8 +24808,9 @@ const executeTestPlan = async ({
24815
24808
  logFileRelativeUrl,
24816
24809
  completedExecutionLogMerging,
24817
24810
  completedExecutionLogAbbreviation,
24818
- rootDirectoryUrl: testDirectoryUrl,
24811
+ rootDirectoryUrl,
24819
24812
  devServerOrigin,
24813
+ sourceDirectoryUrl,
24820
24814
  maxExecutionsInParallel,
24821
24815
  defaultMsAllocatedPerExecution,
24822
24816
  failFast,
@@ -24833,7 +24827,9 @@ const executeTestPlan = async ({
24833
24827
  });
24834
24828
  if (stopDevServerNeeded) {
24835
24829
  // we are expecting ECONNRESET because server will be stopped by the request
24836
- basicFetch(`${devServerOrigin}/__stop__`).catch(e => {
24830
+ basicFetch(`${devServerOrigin}/__stop__`, {
24831
+ rejectUnauthorized: false
24832
+ }).catch(e => {
24837
24833
  if (e.code === "ECONNRESET") {
24838
24834
  return;
24839
24835
  }
@@ -24855,8 +24851,8 @@ const executeTestPlan = async ({
24855
24851
  const htmlCoverageDirectoryIndexFileUrl = `${coverageReportHtmlDirectoryUrl}index.html`;
24856
24852
  logger.info(`-> ${urlToFileSystemPath(htmlCoverageDirectoryIndexFileUrl)}`);
24857
24853
  promises.push(generateCoverageHtmlDirectory(planCoverage, {
24858
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
24859
- coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, coverageReportRootDirectoryUrl),
24854
+ rootDirectoryUrl,
24855
+ coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(coverageReportHtmlDirectoryUrl, rootDirectoryUrl),
24860
24856
  coverageReportSkipEmpty,
24861
24857
  coverageReportSkipFull
24862
24858
  }));
@@ -24904,6 +24900,7 @@ const createRuntimeFromPlaywright = ({
24904
24900
  rootDirectoryUrl,
24905
24901
  fileRelativeUrl,
24906
24902
  devServerOrigin,
24903
+ sourceDirectoryUrl,
24907
24904
  // measurePerformance,
24908
24905
  collectPerformance,
24909
24906
  coverageEnabled = false,
@@ -25010,8 +25007,7 @@ const createRuntimeFromPlaywright = ({
25010
25007
  const fsUrl = moveUrl({
25011
25008
  url: v8CoveragesWithWebUrl.url,
25012
25009
  from: `${devServerOrigin}/`,
25013
- to: rootDirectoryUrl,
25014
- preferAbsolute: true
25010
+ to: sourceDirectoryUrl
25015
25011
  });
25016
25012
  return {
25017
25013
  ...v8CoveragesWithWebUrl,
@@ -25073,8 +25069,19 @@ const createRuntimeFromPlaywright = ({
25073
25069
  result.performance = performance;
25074
25070
  });
25075
25071
  }
25076
- const fileClientUrl = new URL(fileRelativeUrl, `${devServerOrigin}/`).href;
25077
-
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
+ });
25078
25085
  // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-console
25079
25086
  const removeConsoleListener = registerEvent({
25080
25087
  object: page,
@@ -25157,7 +25164,7 @@ const createRuntimeFromPlaywright = ({
25157
25164
  },
25158
25165
  response: async cb => {
25159
25166
  try {
25160
- await page.goto(fileClientUrl, {
25167
+ await page.goto(fileDevServerUrl, {
25161
25168
  timeout: 0
25162
25169
  });
25163
25170
  const returnValue = await page.evaluate( /* eslint-disable no-undef */
@@ -26399,6 +26406,7 @@ const execute = async ({
26399
26406
  handleSIGINT = true,
26400
26407
  logLevel,
26401
26408
  rootDirectoryUrl,
26409
+ sourceDirectoryUrl = rootDirectoryUrl,
26402
26410
  devServerOrigin,
26403
26411
  fileRelativeUrl,
26404
26412
  allocatedMs,
@@ -26428,6 +26436,7 @@ const execute = async ({
26428
26436
  let resultTransformer = result => result;
26429
26437
  runtimeParams = {
26430
26438
  rootDirectoryUrl,
26439
+ sourceDirectoryUrl,
26431
26440
  devServerOrigin,
26432
26441
  fileRelativeUrl,
26433
26442
  ...runtimeParams
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "32.2.3",
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",
@@ -1,6 +1,6 @@
1
1
  export const basicFetch = async (
2
2
  url,
3
- { method = "GET", headers = {} } = {},
3
+ { rejectUnauthorized = true, method = "GET", headers = {} } = {},
4
4
  ) => {
5
5
  let requestModule
6
6
  if (url.startsWith("http:")) {
@@ -14,6 +14,7 @@ export const basicFetch = async (
14
14
 
15
15
  return new Promise((resolve, reject) => {
16
16
  const req = request({
17
+ rejectUnauthorized,
17
18
  hostname: urlObject.hostname,
18
19
  port: urlObject.port,
19
20
  path: urlObject.pathname,
@@ -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,17 +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`,
175
+ { rejectUnauthorized: false },
169
176
  )
170
- if (
171
- testDirectoryUrl !== sourceDirectoryUrl &&
172
- !urlIsInsideOf(testDirectoryUrl, sourceDirectoryUrl)
173
- ) {
174
- throw new Error(
175
- `testDirectoryUrl must be inside sourceDirectoryUrl when running tests on browser(s)`,
176
- )
177
- }
177
+ sourceDirectoryUrl = devServerParams.sourceDirectoryUrl
178
178
  }
179
179
 
180
180
  if (coverageEnabled) {
@@ -213,20 +213,9 @@ export const executeTestPlan = async ({
213
213
  )
214
214
  }
215
215
  }
216
- if (coverageReportRootDirectoryUrl === undefined) {
217
- coverageReportRootDirectoryUrl =
218
- lookupPackageDirectory(testDirectoryUrl)
219
- } else {
220
- coverageReportRootDirectoryUrl = assertAndNormalizeDirectoryUrl(
221
- coverageReportRootDirectoryUrl,
222
- "coverageReportRootDirectoryUrl",
223
- )
224
- }
216
+
225
217
  if (coverageTempDirectoryUrl === undefined) {
226
- coverageTempDirectoryUrl = new URL(
227
- "./.coverage/tmp/",
228
- coverageReportRootDirectoryUrl,
229
- )
218
+ coverageTempDirectoryUrl = new URL("./.coverage/tmp/", rootDirectoryUrl)
230
219
  } else {
231
220
  coverageTempDirectoryUrl = assertAndNormalizeDirectoryUrl(
232
221
  coverageTempDirectoryUrl,
@@ -237,7 +226,7 @@ export const executeTestPlan = async ({
237
226
  if (coverageReportJsonFileUrl === undefined) {
238
227
  coverageReportJsonFileUrl = new URL(
239
228
  "./.coverage/coverage.json",
240
- coverageReportRootDirectoryUrl,
229
+ rootDirectoryUrl,
241
230
  )
242
231
  } else {
243
232
  coverageReportJsonFileUrl = assertAndNormalizeFileUrl(
@@ -250,7 +239,7 @@ export const executeTestPlan = async ({
250
239
  if (coverageReportHtmlDirectoryUrl === undefined) {
251
240
  coverageReportHtmlDirectoryUrl = new URL(
252
241
  "./.coverage/",
253
- coverageReportRootDirectoryUrl,
242
+ rootDirectoryUrl,
254
243
  )
255
244
  } else {
256
245
  coverageReportHtmlDirectoryUrl = assertAndNormalizeDirectoryUrl(
@@ -302,8 +291,15 @@ export const executeTestPlan = async ({
302
291
  }
303
292
 
304
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`)
305
301
 
306
- const result = await executePlan(testPlan, {
302
+ const result = await executeSteps(executionSteps, {
307
303
  signal,
308
304
  handleSIGINT,
309
305
  logger,
@@ -316,8 +312,9 @@ export const executeTestPlan = async ({
316
312
  logFileRelativeUrl,
317
313
  completedExecutionLogMerging,
318
314
  completedExecutionLogAbbreviation,
319
- rootDirectoryUrl: testDirectoryUrl,
315
+ rootDirectoryUrl,
320
316
  devServerOrigin,
317
+ sourceDirectoryUrl,
321
318
 
322
319
  maxExecutionsInParallel,
323
320
  defaultMsAllocatedPerExecution,
@@ -336,7 +333,9 @@ export const executeTestPlan = async ({
336
333
  })
337
334
  if (stopDevServerNeeded) {
338
335
  // we are expecting ECONNRESET because server will be stopped by the request
339
- basicFetch(`${devServerOrigin}/__stop__`).catch((e) => {
336
+ basicFetch(`${devServerOrigin}/__stop__`, {
337
+ rejectUnauthorized: false,
338
+ }).catch((e) => {
340
339
  if (e.code === "ECONNRESET") {
341
340
  return
342
341
  }
@@ -364,10 +363,10 @@ export const executeTestPlan = async ({
364
363
  )
365
364
  promises.push(
366
365
  generateCoverageHtmlDirectory(planCoverage, {
367
- rootDirectoryUrl: coverageReportRootDirectoryUrl,
366
+ rootDirectoryUrl,
368
367
  coverageHtmlDirectoryRelativeUrl: urlToRelativeUrl(
369
368
  coverageReportHtmlDirectoryUrl,
370
- coverageReportRootDirectoryUrl,
369
+ rootDirectoryUrl,
371
370
  ),
372
371
  coverageReportSkipEmpty,
373
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,