testaro 8.0.0 → 8.0.2

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
@@ -6,11 +6,11 @@ Federated accessibility test automation
6
6
 
7
7
  Testaro is a collection of collections of web accessibility tests.
8
8
 
9
- The purpose of Testaro is to provide programmatic access to accessibility tests defined in several test packages and in Testaro itself.
9
+ The purpose of Testaro is to provide programmatic access to accessibility tests defined in several test packages, including Testaro itself.
10
10
 
11
11
  Testaro launches and controls web browsers, performing operations, conducting tests, and recording results.
12
12
 
13
- Testaro is designed to be a workstation-based agent. Testaro can be installed on a workstation running under OS X or Windows, or potentially Ubuntu Linux. The software that uses Testaro can be installed on the same workstation or any other workstation or server. Such other software can perform functions that do not require workstation features, such as:
13
+ Testaro is designed to be a workstation-based agent. Testaro can be installed on a workstation running under OS X or Windows, or potentially Ubuntu Linux. Software that uses Testaro can be installed on the same workstation or any other workstation or server. Such other software can perform functions that do not require workstation features, such as:
14
14
  - Test scheduling
15
15
  - Monitoring
16
16
  - Management of clusters of workstations sharing workloads
@@ -19,7 +19,7 @@ Testaro is designed to be a workstation-based agent. Testaro can be installed on
19
19
  - Converting user specifications into instructions for workstations
20
20
  - Allocating testing responsibilities to human testers
21
21
  - Combining reports from workstations and human testers
22
- - Analyzing and summarizing test results
22
+ - Analyzing and summarizing (e.g., computing scores on the basis of) test results
23
23
  - Sending notifications
24
24
  - Publishing reports
25
25
 
@@ -107,7 +107,7 @@ All of the tests that Testaro can perform are free of cost, except those in the
107
107
 
108
108
  ## Jobs
109
109
 
110
- A request to a workstation to do some work is a _job_.
110
+ A request to Testaro to do some work is a _job_.
111
111
 
112
112
  ## Scripts
113
113
 
@@ -163,11 +163,11 @@ Here is an example of a script:
163
163
  }
164
164
  ```
165
165
 
166
- This script tells Testaro to open a page in the Chromium browser, navigate to `example.com`, and perform the tests in the `alfa` package on that URL, within 65 seconds.
166
+ This script tells Testaro to open a page in the Chromium browser, navigate to `example.com`, and, if the browser is not redirected, perform the tests in the `alfa` package on that URL, within 65 seconds.
167
167
 
168
168
  ### Strictness
169
169
 
170
- If the `strict` property is `true`, Testaro will accept redirections that add or subtract a final slash, but otherwise will treat redirections as failures.
170
+ If the `strict` property is `true`, Testaro will accept redirections that add or subtract a final slash, but otherwise will treat redirections as failures. For example, a redirection from `xyz.com` to `www.xyz.com` or `xyz.com/en` will abort the job.
171
171
 
172
172
  ### Commands
173
173
 
@@ -207,7 +207,7 @@ In this case, Testaro checks the third radio button whose text includes the stri
207
207
 
208
208
  In identifying the target element for a move, Testaro matches the `which` property with the texts of the elements of the applicable type (such as radio buttons). It defines the text of an `input` element as the concatenated texts of its implicit label or explicit labels, if any, plus, for the first input in a `fieldset` element, the text content of the `legend` element of that `fieldset` element. For any other element, Testaro defines the text as the text content of the element.
209
209
 
210
- When multiple elements of the same type have indistinguishable texts, you can include an `index` property to specify the index of the target element, among all those that will match.
210
+ When the texts of multiple elements of the same type will contain the `which` value, you can include an `index` property to specify the index of the target element, among all those that will match.
211
211
 
212
212
  ##### Navigations
213
213
 
@@ -215,7 +215,7 @@ An example of a **navigation** is the command of type `url` above.
215
215
 
216
216
  Once you have included a `url` command in a script, you do not need to add more `url` commands unless you want the browser to visit a different URL.
217
217
 
218
- However, some tests modify web pages. In those cases, Testaro inserts additional `url` acts into the `acts` array, after those tests, to ensure that changes made by one test do not affect subsequent acts.
218
+ However, some tests modify web pages. In those cases, Testaro inserts additional `launch` and `url` act pairs into the `acts` array, after those tests, to ensure that changes made by one test do not affect subsequent acts, if the environment variable `URL_INJECT` has the value `yes`.
219
219
 
220
220
  Another navigation example is:
221
221
 
@@ -258,7 +258,7 @@ Thus, if a command of type `test` runs Continuum, Continuum performs multiple te
258
258
 
259
259
  Every test in Testaro must have:
260
260
  - a property in the `tests` object defined in the `run.js` file, where the property name is the name of the test and the value is a description of the test
261
- - a `.js` file in the `tests` directory, whose name base is the name of the test
261
+ - a `.js` file, defining the test, in the `tests` directory, whose name base is the name of the test
262
262
 
263
263
  The `commands.js` file (described in detail below) contains this specification for any `test` command:
264
264
 
@@ -272,7 +272,7 @@ test: [
272
272
  ],
273
273
  ```
274
274
 
275
- That means that a test (i.e. a command with a `type` property having the value `'test'`) must have a string-valued `which` property and may optionally have a string-valued `what` property.
275
+ That means that a test (i.e. a command with a `type` property having the value `'test'`) must have a string-valued `which` property naming a test and may optionally have a string-valued `what` property describing the test.
276
276
 
277
277
  If a particular test either must have or may have any other properties, those properties must be specified in the `tests` property in `commands.js`.
278
278
 
@@ -334,7 +334,7 @@ Example:
334
334
  }
335
335
  ```
336
336
 
337
- The reason for this is that the Tenon API operates asynchronously. You ask it to perform a test, and it puts your request into a queue. To learn whether Tenon has completed your test, you make a status request. You can continue making status requests until Tenon replies that your test has been completed. Then you submit a request for the test result, and Tenon replies with the result. (As of May 2022, status requests were observed to misreport still-running tests as completed. The `tenon` test works around that by requesting only the result and using the response to determine whether the tests have been completed.)
337
+ The reason for this is that the Tenon API operates asynchronously. You ask it to perform a test, and it puts your request into a queue. To learn whether Tenon has completed your test, you make a status request. You can continue making status requests until Tenon replies that your test has been completed. Then you submit a request for the test result, and Tenon replies with the result. (As of May 2022, however, status requests were observed to misreport still-running tests as completed. The `tenon` test works around that by requesting only the result and using the response to determine whether the tests have been completed.)
338
338
 
339
339
  Tenon says that tests are typically completed in 3 to 6 seconds but that the latency can be longer, depending on demand.
340
340
 
@@ -390,6 +390,8 @@ The changes in `htmlcs/HTMLCS.js` are:
390
390
 
391
391
  If a `wave` test is included in the script, an environment variable named `WAVE_KEY` must exist, with your WAVE API key as its value. You can get it from [WebAIM](https://wave.webaim.org/api/).
392
392
 
393
+ The `wave` API does not accept a transmitted document for testing. WAVE must be given only a URL, which it then visits to perform its tests. Therefore, you cannot manipulate a page and then have WAVE test it, or ask WAVE to test a page that cannot be reached directly with a URL.
394
+
393
395
  ###### BBC Accessibility Standards Checker
394
396
 
395
397
  The BBC Accessibility Standards Checker has obsolete dependencies with security vulnerabilities. Therefore, it is not used as a dependency of Testaro. Instead, 6 of its tests are reimplemented, in some cases with revisions, as Testaro tests. They are drawn from the 18 automated tests of the Checker. The other 12 tests were found too duplicative of other tests to justify reimplementation.
@@ -495,17 +497,36 @@ A typical use for an `expect` property is checking the correctness of a Testaro
495
497
 
496
498
  ## Samples
497
499
 
498
- The `sampleScripts` directory contains examples of scripts. If you wish to use any of them, you can give `SCRIPTDIR` the value `'sampleScripts'`. Then execute `node high sss` to run the `sss` script. The `high` module will create a job, run the script, and save the report in the directory that you have specified with the `REPORTDIR` environment variable.
500
+ The `samples` directory contains examples of scripts. If you wish to use any of them, you can give `SCRIPTDIR` the value `'samples'`.
499
501
 
500
502
  ## Execution
501
503
 
502
- ### Invocation
504
+ ### Introduction
505
+
506
+ Testaro can be called by modules and by users.
507
+
508
+ ### Functions
503
509
 
504
- There are three methods for using Testaro.
510
+ Testaro contains these modules that export executable functions:
511
+ - `run.js` exports `doJob` for low-level execution.
512
+ - `high.js` exports `runJob` for high-level execution.
513
+ - `watch.js` exports `cycle` for watch-triggered execution.
514
+
515
+ #### Imports
516
+
517
+ Before a module can execute a Testaro function, it must import that function from the module that exports it. A Testaro module can import function `f` from module `m` with the statement
518
+
519
+ ```javascript
520
+ const {f} = require('./m');`
521
+ ```
522
+
523
+ The argument of `require` is a path relative to the directory of the module in which this code appears. If the module is in a subdirectory, `./m` will need to be revised. In an executor within `validation/executors`, it must be revised to `../../m`.
524
+
525
+ A module in another Node.js package that has Testaro as a dependency can execute the same statements, except changing `'./m'` to `'testaro/m'`.
505
526
 
506
527
  #### Low-level
507
528
 
508
- A module in this package can invoke Testaro with this pattern:
529
+ Low-level execution is designed for a module to create a job and make Testaro run it, as follows:
509
530
 
510
531
  ```javascript
511
532
  const report = {
@@ -520,87 +541,81 @@ doJob(report)
520
541
 
521
542
  Replace `{…}` with a script object, like the example script shown above.
522
543
 
523
- The argument of `require` is a path relative to the directory of the module in which this code appears. If the module is in a subdirectory, `./run` will need to be revised. In an executor within `validation/executors`, it must be revised to `../../run`.
524
-
525
- Another Node.js package that has Testaro as a dependency can execute the same statements, except changing `'./run'` to `'testaro/run'`.
526
-
527
544
  Testaro will run the script and modify the properties of the `report` object. When Testaro finishes, the `log`, `acts`, and other properties of `report` will contain the results. The final statement can further process the `report` object as desired in the `then` callback.
528
545
 
529
546
  #### High-level
530
547
 
531
- High-level invocation allows you to run Testaro with a one-line command.
548
+ High-level execution is designed for either modules or users.
532
549
 
533
- Make sure that you have defined these environment variables, with absolute or relative paths to directories as their values:
534
- - `SCRIPTDIR`
535
- - `REPORTDIR`
550
+ Execution by a module:
536
551
 
537
- Relative paths must be relative to the Testaro project directory. For example, if the script directory is `scripts` in a `testing` directory that is a sibling of the Testaro directory, then a relative-path `SCRIPTDIR` must have the value `../testing/scripts`.
552
+ ```javaScript
553
+ const {runJob} = require('./high');
554
+ runJob('tp123');
555
+ ```
538
556
 
539
- Also ensure that Testaro can read all those directories and write to `REPORTDIR`.
557
+ Execution by a user:
540
558
 
541
- Place a script into `SCRIPTDIR`. It should be named with a `.json` extension.
559
+ ```bash
560
+ node call high tp123
561
+ ```
542
562
 
543
- Then execute the statement `node high scriptID`, replacing `scriptID` with the `id` value of the script, which must also be the base of the name of the script file.
563
+ In either case, replace `tp123` with the base of the name of a script.
544
564
 
545
- The `high` module will call the `runJob` function of the `create` module, which in turn will call the `doJob` function of the `run` module. The result will be saved in a report file in the `REPORTDIR` directory. The report file will be named with a unique timestamp and suffixed with a `.json` extension. The base of the report file’s name will be the same timestamp, suffixed with `--scriptID`, where `scriptID` is the value of the `id` property of the script. For example, if you execute `node high tp18-wiktionary`, you might get a report named `enp45j-tp18-wiktionary.json` file deposited into `REPORTDIR`.
565
+ Testaro will find the named script (e.g., `tp123.json`) in the `SCRIPTDIR` directory and write the report in the `REPORTDIR` directory.
546
566
 
547
567
  #### Watch
548
568
 
549
- In watch mode, Testaro periodically checks for a script to be run by it. When such a script exists, Testaro runs it, produces a report in JSON format, and disposes of the report as specified.
569
+ Watch execution is designed for either modules or users.
550
570
 
551
- Testaro checks periodically. The interval between checks, in seconds, is specified by an `INTERVAL` environment variable.
571
+ In watch mode, Testaro periodically checks for a script to be run by it. When such a script exists, Testaro runs it and provides a report. Testaro may continue watching after the first report, or may quit.
552
572
 
553
- After Testaro starts watching, its behavior depends on the environment variable `WATCH_FOREVER`. If its value is `true`, watching continues indefinitely. If its value is `false` or it has no value, watching stops after the first script is found and run.
573
+ Execution by a module:
554
574
 
555
- To make Testaro start watching, execute the statement `node watch`.
575
+ ```javaScript
576
+ const {cycle} = require('./watch');
577
+ cycle(true, true, 30);
578
+ ```
556
579
 
557
- There are two ways for Testaro to watch for jobs.
580
+ Execution by a user:
558
581
 
559
- ##### Directory watch
582
+ ```javaScript
583
+ node call watch true true 30
584
+ ```
560
585
 
561
- With directory watch, Testaro checks whether the watch directory (`WATCHDIR`) in its host’s filesystem contains a script.
586
+ The arguments passed to `cycle` by either of these invocations are:
587
+ - whether to watch a directory (true) or the network (false)
588
+ - whether to continue watching indefinitely after the first report (true or false)
589
+ - how many seconds to wait before checking again (a nonnegative number)
562
590
 
563
- When Testaro finds one or more scripts in the watch directory, Testaro runs the first script, writes the report into the report directory, and moves the script into the done directory (`DONEDIR`).
591
+ ##### Directory watch
564
592
 
565
- Testaro suspends checking while it is running a script. Therefore, even though the currently running script file remains in thde watch directory, Testaro will not try to run it again.
593
+ With directory watch, Testaro checks whether the watch directory (`WATCHDIR`) in its host’s filesystem contains a script.
566
594
 
567
- Since Testaro runs the first script (i.e. the script whose name is first in ASCII order), whoever populates the directory with script files has control over the order in which Testaro runs them. For example, to force a new script to be run before the already waiting scripts, one can give it a filename that comes before that of the first waiting script.
595
+ When Testaro finds one or more scripts in the watch directory, Testaro runs the first script, writes the report into the `REPORTDIR` directory, and moves the script into the `DONEDIR` directory.
568
596
 
569
- In order to make directory watching possible, you must define these environment variables:
570
- - `WATCH_TYPE=dir`
571
- - `INTERVAL`
572
- - `WATCH_FOREVER` (`=true` or `=false`)
573
- - `REPORTDIR`
574
- - `WATCHDIR`
575
- - `DONEDIR`
597
+ Since Testaro runs the first script (i.e. the script whose name is first in ASCII order), whoever populates the `WATCHDIR` directory with script files has control over the order in which Testaro runs them. For example, to force a new script to be run before the already waiting scripts, one can give it a filename that comes before that of the first waiting script.
576
598
 
577
599
  ##### Network watch
578
600
 
579
- Network watching is based on these assumptions:
601
+ Network watching is designed for a situation in which:
580
602
  - A managing server may be able to give work to multiple workstations that run Testaro.
581
603
  - A workstation running Testaro can contact a managing server, but the server may not be able to contact a workstation.
582
604
  - The functions of Testaro are limited to those requiring workstation features.
583
605
 
584
- Given these assumptions, with network watch, the initiator of an interaction is Testaro. When Testaro is available, it requests a script from a server. If the response is a JSON representation of a script, Testaro runs the script and sends the report to the server.
585
-
586
- In order to make network watching possible, you must define these environment variables:
587
- - `WATCH_TYPE=net`
588
- - `INTERVAL`
589
- - `WATCH_FOREVER` (`=true` or `=false`)
590
- - `PROTOCOL` (`=http` or `=https`)
591
- - `WATCH_URL`
592
- - `REPORT_URL`
593
- - `TESTARO_ID`
606
+ With network watch, the initiator of an interaction is Testaro, not the server. When Testaro is available, it requests a script from a server. If the response is a JSON representation of a script, Testaro runs the script and sends the report to the server.
594
607
 
595
- If multiple workstations run Testaro and do work for the same server, each must have a different `TESTARO_ID`.
608
+ If multiple workstations run Testaro and do work for the same server, each must have a different value on the `AGENT` environment variable.
596
609
 
597
610
  ### Environment variables
598
611
 
612
+ Variables named above in upper-case letters are environment variables used by various modules.
613
+
599
614
  The `text` command can interpolate the value of an environment variable into text that it enters on a page, as documented in the `commands.js` file.
600
615
 
601
616
  Before executing a Testaro script, you can optionally also set the environment variables `DEBUG` (to `'true'` or anything else) and/or `WAITS` (to a non-negative integer). The effects of these variables are described in the `index.js` file.
602
617
 
603
- You may store these environment variables in an untracked `.env` file if you wish, and Testaro will recognize them.
618
+ You may store environment variables in an untracked `.env` file if you wish, and Testaro will recognize them.
604
619
 
605
620
  ## Validation
606
621
 
@@ -611,17 +626,15 @@ Testaro and its custom tests can be validated with the _executors_ located in th
611
626
  The executors are:
612
627
 
613
628
  - `low`: validates low-level invocation
614
- - `high1`: validates high-level invocation of a script without a batch
615
- - `high2`: validates high-level invocation of a script with a batch
629
+ - `high`: validates high-level invocation of a script
616
630
  - `watchDir`: validates directory watching
617
631
  - `watchNet`: validates network watching
618
- - `tests`: validates all the custom tests (not the test packages)
619
-
620
- To validate any single Testaro test `xyz`, enter the statement `npm run test1 xyz`.
632
+ - `test`: validates a Testaro test
633
+ - `tests`: validates all the Testaro tests
621
634
 
622
- To validate all of the Testaro tests, enter the statement `npm test`.
635
+ To validate any single Testaro test `xyz`, enter the statement `npm test xyz`.
623
636
 
624
- To execute any executor `xyz` other than `test` or `tests`, call it with the statement `node validation/executors/xyz`.
637
+ To execute any other executor `xyz`, call it with the statement `node run xyz`.
625
638
 
626
639
  The `tests` executor makes use of the scripts in the `validation/tests/scripts` directory, and they, in turn, run tests on HTML files in the `validation/tests/targets` directory.
627
640
 
@@ -651,7 +664,7 @@ The Playwright “Receives Events” actionability check does **not** check whet
651
664
 
652
665
  Test packages sometimes do redundant testing, in that two or more packages test for the same issues, although such duplications are not necessarily perfect. This fact creates three problems:
653
666
  - One cannot be confident in excluding some tests of some packages on the assumption that they perfectly duplicate tests of other packages.
654
- - The Testaro report from a script documents each package’s results separately, so a single defect may be documented in multiple locations within the report, making the consumption of the report inefficient.
667
+ - The Testaro report from a script documents each package’s results separately, so a single defect may be documented in multiple locations within the report, making the direct consumption of the report inefficient.
655
668
  - An effort to aggregate the results into a single score may distort the scores by inflating the weights of defects that happen to be discovered by multiple packages.
656
669
 
657
670
  The tests provided with Testaro do not exclude any apparently duplicative tests from packages.
@@ -670,11 +683,14 @@ The files in the `temp` directory are presumed ephemeral and are not tracked by
670
683
  ## Related packages
671
684
 
672
685
  [Testilo](https://www.npmjs.com/package/testilo) is an application that:
686
+ - aims a script at a host by modifying the `url` commands
673
687
  - merges batches of hosts into scripts to produce multiple scripts
674
688
  - produces scores and adds them to the JSON report files of Testaro
675
689
  - produces human-oriented HTML digests from scored reports
676
690
  - produces human-oriented HTML reports comparing the scores of hosts
677
691
 
692
+ Testilo contains procedures that reorganize report data by defect rather than test package, and that compensate for duplicative tests when computing scores.
693
+
678
694
  Testaro is derived from [Autotest](https://github.com/jrpool/autotest).
679
695
 
680
696
  Testaro omits some functionalities of Autotest, such as:
@@ -1,14 +1,14 @@
1
1
  /*
2
- do.js
2
+ call.js
3
3
  Invokes Testaro modules with arguments.
4
4
  This is the universal module for use of Testaro from a command line.
5
5
  Arguments:
6
6
  0. function to execute.
7
7
  1+. arguments to pass to the function.
8
8
  Usage examples:
9
- node do high script454
10
- node do watch dir once 30
11
- node do watch net forever 60
9
+ node call high script454
10
+ node call watch dir once 30
11
+ node call watch net forever 60
12
12
  */
13
13
 
14
14
  // ########## IMPORTS
@@ -29,16 +29,16 @@ const reportDir = process.env.REPORTDIR;
29
29
  // ########## FUNCTIONS
30
30
 
31
31
  // Fulfills a high-level testing request.
32
- const doHigh = async scriptID => {
32
+ const callHigh = async scriptID => {
33
33
  await runJob(scriptID);
34
34
  console.log(`Job completed and report ${scriptID}.json saved in ${reportDir}`);
35
35
  };
36
36
  // Starts a watch.
37
- const doWatch = async (isDirWatch, isForever, interval) => {
37
+ const callWatch = async (isDirWatch, isForever, interval) => {
38
38
  console.log(
39
- `Starting a ${isForever ? 'repeating' : 'one-time'} ${isDirWatch ? 'directory' : 'network'} watch`
39
+ `Starting a ${isForever === 'true' ? 'repeating' : 'one-time'} ${isDirWatch === 'true' ? 'directory' : 'network'} watch`
40
40
  );
41
- await cycle(isDirWatch, isForever, interval);
41
+ await cycle(isDirWatch === 'true', isForever === 'true', Number.parseInt(interval, 10));
42
42
  console.log('Watching ended');
43
43
  };
44
44
 
@@ -46,13 +46,13 @@ const doWatch = async (isDirWatch, isForever, interval) => {
46
46
 
47
47
  // Execute the requested function.
48
48
  if (fn === 'high' && fnArgs.length === 1) {
49
- doHigh(fnArgs)
49
+ callHigh(fnArgs)
50
50
  .then(() => {
51
51
  console.log('Execution completed');
52
52
  });
53
53
  }
54
54
  else if (fn === 'watch' && fnArgs.length === 3) {
55
- doWatch(... fnArgs)
55
+ callWatch(... fnArgs)
56
56
  .then(() => {
57
57
  console.log('Execution completed');
58
58
  });
package/high.js CHANGED
@@ -17,7 +17,6 @@ const {doJob} = require('./run');
17
17
 
18
18
  const scriptDir = process.env.SCRIPTDIR;
19
19
  const reportDir = process.env.REPORTDIR;
20
- const scriptID = process.argv[2];
21
20
 
22
21
  // ########## VARIABLES
23
22
 
@@ -27,7 +26,7 @@ let timeLimit = 300;
27
26
  // ########## FUNCTIONS
28
27
 
29
28
  // Performs a file-based job and writes a report file.
30
- const runJob = async scriptID => {
29
+ exports.runJob = async scriptID => {
31
30
  try {
32
31
  const scriptJSON = await fs.readFile(`${scriptDir}/${scriptID}.json`, 'utf8');
33
32
  const script = JSON.parse(scriptJSON);
@@ -49,16 +48,3 @@ const runJob = async scriptID => {
49
48
  console.log(`ERROR running job (${error.message})\n${error.stack}`);
50
49
  }
51
50
  };
52
-
53
- // ########## OPERATION
54
-
55
- // If this module was called with a scriptID argument:
56
- if (scriptID) {
57
- // Run the script and write a report.
58
- runJob(scriptID);
59
- }
60
- // Otherwise, i.e. if it was required by another module:
61
- else {
62
- // Export runJob so the other module can call it.
63
- exports.runJob = runJob;
64
- }
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "8.0.0",
3
+ "version": "8.0.2",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "node validation/executors/tests",
8
- "test1": "node validation/executors/test"
7
+ "tests": "node validation/executors/tests",
8
+ "test": "node validation/executors/test",
9
+ "low": "node validation/executors/low",
10
+ "high": "node validation/executors/high",
11
+ "watchDir": "node validation/executors/watchDir",
12
+ "watchNet": "node validation/executors/watchdNet"
9
13
  },
10
14
  "repository": {
11
15
  "type": "git",