Skip to content

Commit 65e5ff7

Browse files
authored
test: more CLI tests (#52)
* fix: normalizeRepositoryUrl to properly handle git+ssh://git@github.com: URLs * chore: setup jest in shared package * refactor: extract helpers from common.ts to yamlUtils.ts * docs: updated docstrings for common.ts key functions * test: unit tests for utility functions * ci: run unit tests of shared package workflow * fix(docs): default value of --dev-deps-mode in CLI's README.md * docs: more precise description of from-workspace-only --tm mode * fix(docs): fix of license-kit README.md * chore: add jest-extended matchers to node-example * test: added more integration tests to CLI report command test suite * chore: move integration tests to __tests__ as per project convention * ci: rename shared package unit tests workflow * chore: add test workflows badges to README.md * ci: rename shared package workflow to describe unit tests * docs: proper badge name for shared unit tests workflow
1 parent a25da5b commit 65e5ff7

22 files changed

Lines changed: 642 additions & 148 deletions
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Unit tests - React Native Legal Shared package
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- '.github/workflows/test-unit-shared.yml'
9+
- 'packages/shared/**/*.[tj]sx?'
10+
11+
push:
12+
branches:
13+
- main
14+
15+
workflow_dispatch:
16+
17+
concurrency: ${{ github.workflow }}-${{ github.ref }}
18+
19+
jobs:
20+
unit-test-shared:
21+
name: Run unit tests for @callstack/react-native-legal-shared
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout Repo
25+
uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
29+
- name: Setup
30+
uses: ./.github/actions/setup
31+
32+
- name: Run unit tests
33+
run: yarn workspace @callstack/react-native-legal-shared test

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010

1111
---
1212

13-
![E2E tests - Android](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-e2e-android.yaml/badge.svg)
14-
![E2E tests - iOS](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-e2e-ios.yaml/badge.svg)
1513
![Release](https://github.com/callstackincubator/react-native-legal/actions/workflows/release.yml/badge.svg)
1614
![Deploy Docs](https://github.com/callstackincubator/react-native-legal/actions/workflows/deploy-docs.yml/badge.svg)
15+
![Integration tests - License Kit (Node)](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-integration-node.yml/badge.svg)
16+
![Unit tests - React Native Legal Shared](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-unit-shared.yml/badge.svg)
17+
![E2E tests - Android](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-e2e-android.yaml/badge.svg)
18+
![E2E tests - iOS](https://github.com/callstackincubator/react-native-legal/actions/workflows/test-e2e-ios.yaml/badge.svg)
1719

1820
- [Documentation](#documentation)
1921
- [Installation](#installation)

docs/docs/docs/standalone-cli.mdx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,24 @@ Exit codes:
6868
- `1` - strong copyleft licenses found
6969
- `2` - weak copyleft licenses found (if `--error-on-weak` is set)
7070

71-
| Flag / Option | Description | Default |
72-
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- |
73-
| `--tm, --transitive-deps-mode [mode]` | Controls, which transitive dependencies are included: <ul><li>`'all'`</li> <li>`'from-external-only'` (only transitive dependencies of direct dependencies specified by non-workspace:... specifiers)</li> <li>`'from-workspace-only'` (only transitive dependencies of direct dependencies specified by `workspace:` specifier)</li> <li>`'none'`</li></ul> | `'all'` |
74-
| `--dm, --dev-deps-mode [mode]` | <ul><li>`'root-only'` (only direct devDependencies from the scanned project's root package.json)</li> <li>`'none'`</li></ul> | `'root-only'` |
75-
| `--root [path]` | Path to the root of your project | Current working directory |
76-
| `--error-on-weak` | Exit with error code if weak copyleft licenses are found | `false` |
71+
| Flag / Option | Description | Default |
72+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
73+
| `--tm, --transitive-deps-mode [mode]` | Controls, which transitive dependencies are included: <ul><li>`'all'`</li> <li>`'from-external-only'` (only transitive dependencies of direct dependencies specified by non-workspace:... specifiers)</li> <li>`'from-workspace-only'` (only direct dependencies of direct dependencies specified by `workspace:` specifier)</li> <li>`'none'`</li></ul> | `'all'` |
74+
| `--dm, --dev-deps-mode [mode]` | <ul><li>`'root-only'` (only direct devDependencies from the scanned project's root package.json)</li> <li>`'none'`</li></ul> | `'root-only'` |
75+
| `--root [path]` | Path to the root of your project | Current working directory |
76+
| `--error-on-weak` | Exit with error code if weak copyleft licenses are found | `false` |
7777

7878
#### Command: `report`
7979

8080
Generates a licenses report in the specified format. The output can be written to `stdout` (default) or a file.
8181

82-
| Flag / Option | Description | Default |
83-
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- |
84-
| `--tm, --transitive-deps-mode [mode]` | Controls, which transitive dependencies are included: <ul><li>`'all'`</li> <li>`'from-external-only'` (only transitive dependencies of direct dependencies specified by non-workspace:... specifiers)</li> <li>`'from-workspace-only'` (only transitive dependencies of direct dependencies specified by `workspace:` specifier)</li> <li>`'none'`</li></ul> | `'all'` |
85-
| `--dm, --dev-deps-mode [mode]` | <ul><li>`'root-only'` (only direct devDependencies from the scanned project's root package.json)</li> <li>`'none'`</li></ul> | `'root-only'` |
86-
| `--root [path]` | Path to the root of your project | Current working directory |
87-
| `--format [type]` | Output format, one of: `'json'`, `'about-json'` (AboutLibraries-compatible), `'text'`, `'markdown'` | `'json'` |
88-
| `--output [path]` | Where to write the output - either `'stdout'` or a path to an output file | `'stdout'` |
82+
| Flag / Option | Description | Default |
83+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
84+
| `--tm, --transitive-deps-mode [mode]` | Controls, which transitive dependencies are included: <ul><li>`'all'`</li> <li>`'from-external-only'` (only transitive dependencies of direct dependencies specified by non-workspace:... specifiers)</li> <li>`'from-workspace-only'` (only direct dependencies of direct dependencies specified by `workspace:` specifier)</li> <li>`'none'`</li></ul> | `'all'` |
85+
| `--dm, --dev-deps-mode [mode]` | <ul><li>`'root-only'` (only direct devDependencies from the scanned project's root package.json)</li> <li>`'none'`</li></ul> | `'root-only'` |
86+
| `--root [path]` | Path to the root of your project | Current working directory |
87+
| `--format [type]` | Output format, one of: `'json'`, `'about-json'` (AboutLibraries-compatible), `'text'`, `'markdown'` | `'json'` |
88+
| `--output [path]` | Where to write the output - either `'stdout'` or a path to an output file | `'stdout'` |
8989

9090
#### Command: `help`
9191

File renamed without changes.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import child_process from 'node:child_process';
2+
3+
import {
4+
dependencies as licenseKitDependenciesObj,
5+
devDependencies as licenseKitDevDependenciesObj,
6+
} from '../../../packages/license-kit/package.json';
7+
import {
8+
dependencies as sharedDependenciesObj,
9+
devDependencies as sharedDevDependenciesObj,
10+
} from '../../../packages/shared/package.json';
11+
import { dependencies as dependenciesObj, devDependencies as devDependenciesObj } from '../package.json';
12+
13+
const dependencies = Object.keys(dependenciesObj).sort();
14+
const devDependencies = Object.keys(devDependenciesObj).sort();
15+
const licenseKitDependencies = Object.keys(licenseKitDependenciesObj).sort();
16+
const licenseKitDevDependencies = Object.keys(licenseKitDevDependenciesObj).sort();
17+
const sharedDependencies = Object.keys(sharedDependenciesObj).sort();
18+
const sharedDevDependencies = Object.keys(sharedDevDependenciesObj).sort();
19+
20+
async function runReportCommandForJsonOutput(args: string[] = []) {
21+
const command = `yarn workspace license-kit-node-example report${args ? ` ${args.join(' ')}` : ''}`;
22+
23+
const output = await new Promise<string>((resolve) => {
24+
child_process.exec(command, (_, stdout) => {
25+
resolve(stdout);
26+
});
27+
});
28+
29+
return JSON.parse(output);
30+
}
31+
32+
describe('license-kit report', () => {
33+
it('including transitive deps, with dev deps with default settings', async () => {
34+
const json = await runReportCommandForJsonOutput();
35+
36+
expect(json['dhtmlx-gantt'].type).toMatch('GPL-2.0');
37+
expect(json['is-even'].content).toMatch('MIT License');
38+
expect(json['is-even'].type).toMatch('MIT');
39+
expect(json.mariadb.content).toMatch('GNU LESSER GENERAL PUBLIC LICENSE');
40+
expect(json.mariadb.type).toMatch('LGPL-2.1-or-later');
41+
expect(json.zustand.content).toMatch('MIT License');
42+
expect(json.zustand.type).toMatch('MIT');
43+
});
44+
45+
it('without transitive deps and without dev deps', async () => {
46+
const json = await runReportCommandForJsonOutput(['--dev-deps-mode', 'none', '--transitive-deps-mode', 'none']);
47+
48+
expect(Object.keys(json).toSorted()).toEqual(dependencies.toSorted());
49+
});
50+
51+
it('without transitive deps and with root-only dev deps', async () => {
52+
const json = await runReportCommandForJsonOutput([
53+
'--dev-deps-mode',
54+
'root-only',
55+
'--transitive-deps-mode',
56+
'none',
57+
]);
58+
59+
expect(Object.keys(json).toSorted()).toEqual(Array.from(new Set([...dependencies, ...devDependencies])).toSorted());
60+
});
61+
62+
it('with root-only dev deps and with transitive dependencies of workspace-specifier-only dependencies', async () => {
63+
const json = await runReportCommandForJsonOutput([
64+
'--dev-deps-mode',
65+
'root-only',
66+
'--transitive-deps-mode',
67+
'from-workspace-only',
68+
]);
69+
70+
expect(Object.keys(json).toSorted()).toEqual(
71+
Array.from(new Set([...dependencies, ...devDependencies, ...licenseKitDependencies])).toSorted(),
72+
);
73+
});
74+
75+
it('without dev deps and with transitive dependencies of external dependencies only', async () => {
76+
const json = await runReportCommandForJsonOutput([
77+
'--dev-deps-mode',
78+
'none',
79+
'--transitive-deps-mode',
80+
'from-external-only',
81+
]);
82+
83+
const resultKeys = Object.keys(json);
84+
85+
expect(resultKeys.toSorted()).toEqual([
86+
'@types/geojson',
87+
'@types/node',
88+
'denque',
89+
'dhtmlx-gantt',
90+
'iconv-lite',
91+
'is-even',
92+
'is-number',
93+
'is-odd',
94+
'lru-cache',
95+
'mariadb',
96+
'safer-buffer',
97+
'undici-types',
98+
'yallist',
99+
'zustand',
100+
]);
101+
102+
expect(resultKeys).not.toIncludeAllMembers(licenseKitDependencies);
103+
expect(resultKeys).not.toIncludeAllMembers(licenseKitDevDependencies);
104+
});
105+
106+
it('with root-only dev deps and with transitive dependencies of external dependencies only', async () => {
107+
const json = await runReportCommandForJsonOutput([
108+
'--dev-deps-mode',
109+
'root-only',
110+
'--transitive-deps-mode',
111+
'from-external-only',
112+
]);
113+
114+
const resultKeys = Object.keys(json);
115+
116+
expect(resultKeys).toContain('license-kit');
117+
118+
// this time, the result should also include all dependencies of the direct devDependencies of the root package.json
119+
expect(resultKeys.length).toBeGreaterThan(200);
120+
expect(resultKeys).toIncludeAllMembers([
121+
'@babel/plugin-transform-async-to-generator',
122+
'@babel/plugin-transform-block-scoped-functions',
123+
'@babel/plugin-transform-block-scoping',
124+
'@babel/plugin-transform-class-properties',
125+
'@babel/helper-create-class-features-plugin',
126+
'@babel/helper-member-expression-to-functions',
127+
]);
128+
129+
expect(resultKeys).not.toIncludeAllMembers(licenseKitDependencies);
130+
expect(resultKeys).not.toIncludeAllMembers(licenseKitDevDependencies);
131+
132+
// note: sharedDependencies should not be included, yet they contain glob, which is a transitive dependency of something else
133+
expect(resultKeys).not.toIncludeAllMembers(sharedDevDependencies);
134+
});
135+
136+
it('with root-only dev deps and with all transitive dependencies', async () => {
137+
const json = await runReportCommandForJsonOutput(['--dev-deps-mode', 'root-only', '--transitive-deps-mode', 'all']);
138+
139+
const resultKeys = Object.keys(json);
140+
141+
// this time, the result should also include all transitive dependencies of direct devDependencies of the root package.json,
142+
// such as that of license-kit itself (which is a direct devDependency of the root package.json)
143+
expect(resultKeys.length).toBeGreaterThan(300);
144+
expect(resultKeys).toContain('license-kit');
145+
expect(resultKeys).toContain('@callstack/react-native-legal-shared');
146+
147+
expect(resultKeys).toIncludeAllMembers(licenseKitDependencies);
148+
expect(resultKeys).not.toIncludeAllMembers(licenseKitDevDependencies);
149+
150+
expect(resultKeys).toIncludeAllMembers(sharedDependencies);
151+
expect(resultKeys).not.toIncludeAllMembers(sharedDevDependencies);
152+
});
153+
});

examples/node-example/global.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="jest-extended" />
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @type {import('jest').Config} */
22
const config = {
33
testEnvironment: 'node',
4+
setupFilesAfterEnv: ['jest-extended/all'],
45
};
56

67
module.exports = config;

examples/node-example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@types/jest": "^29.5.5",
2222
"babel-jest": "^29.7.0",
2323
"jest": "^29.7.0",
24+
"jest-extended": "^6.0.0",
2425
"license-kit": "workspace:*"
2526
}
2627
}

examples/node-example/test/report.spec.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)