react-native-coverage 0.1.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/Coverage.podspec +20 -0
- package/LICENSE +202 -0
- package/README.md +125 -0
- package/android/build.gradle +51 -0
- package/android/rn-coverage-jacoco.gradle +132 -0
- package/android/rn-coverage.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/coverage/CoverageModule.kt +53 -0
- package/android/src/main/java/com/coverage/CoveragePackage.kt +35 -0
- package/app.plugin.js +1 -0
- package/bin/rn-coverage.js +11 -0
- package/cocoapods/coverage_post_install.rb +197 -0
- package/docs/cli.md +51 -0
- package/docs/config.md +27 -0
- package/docs/index.md +16 -0
- package/docs/integration/android.md +83 -0
- package/docs/integration/ci-appium.md +41 -0
- package/docs/integration/e2e-timing.md +20 -0
- package/docs/integration/ios.md +77 -0
- package/docs/integration/js.md +38 -0
- package/docs/pattern-c.md +5 -0
- package/docs/releasing.md +88 -0
- package/docs.json +24 -0
- package/ios/Coverage.h +5 -0
- package/ios/Coverage.mm +47 -0
- package/ios/CoverageConfig.h +21 -0
- package/ios/CoverageProfile.h +22 -0
- package/ios/CoverageProfile.mm +358 -0
- package/lib/module/NativeCoverage.js +10 -0
- package/lib/module/NativeCoverage.js.map +1 -0
- package/lib/module/assert-coverage.js +181 -0
- package/lib/module/assert-coverage.js.map +1 -0
- package/lib/module/cli/index.js +229 -0
- package/lib/module/cli/index.js.map +1 -0
- package/lib/module/config.js +83 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/exit-codes.js +22 -0
- package/lib/module/exit-codes.js.map +1 -0
- package/lib/module/index.js +35 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/js-coverage.js +172 -0
- package/lib/module/js-coverage.js.map +1 -0
- package/lib/module/node.js +15 -0
- package/lib/module/node.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/path-rewrite.js +44 -0
- package/lib/module/path-rewrite.js.map +1 -0
- package/lib/module/process-ios-native-coverage.js +258 -0
- package/lib/module/process-ios-native-coverage.js.map +1 -0
- package/lib/module/pull-native-coverage.js +210 -0
- package/lib/module/pull-native-coverage.js.map +1 -0
- package/lib/typescript/example-dynamic/App.d.ts +6 -0
- package/lib/typescript/example-dynamic/App.d.ts.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeCoverage.d.ts +16 -0
- package/lib/typescript/src/NativeCoverage.d.ts.map +1 -0
- package/lib/typescript/src/assert-coverage.d.ts +53 -0
- package/lib/typescript/src/assert-coverage.d.ts.map +1 -0
- package/lib/typescript/src/cli/index.d.ts +4 -0
- package/lib/typescript/src/cli/index.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +93 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/exit-codes.d.ts +16 -0
- package/lib/typescript/src/exit-codes.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +16 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/js-coverage.d.ts +36 -0
- package/lib/typescript/src/js-coverage.d.ts.map +1 -0
- package/lib/typescript/src/node.d.ts +14 -0
- package/lib/typescript/src/node.d.ts.map +1 -0
- package/lib/typescript/src/path-rewrite.d.ts +12 -0
- package/lib/typescript/src/path-rewrite.d.ts.map +1 -0
- package/lib/typescript/src/process-ios-native-coverage.d.ts +61 -0
- package/lib/typescript/src/process-ios-native-coverage.d.ts.map +1 -0
- package/lib/typescript/src/pull-native-coverage.d.ts +38 -0
- package/lib/typescript/src/pull-native-coverage.d.ts.map +1 -0
- package/package.json +184 -0
- package/plugin/build/index.d.ts +21 -0
- package/plugin/build/index.js +153 -0
- package/react-native-coverage.config.js.example +47 -0
- package/react-native.config.js +9 -0
- package/src/NativeCoverage.ts +16 -0
- package/src/assert-coverage.ts +260 -0
- package/src/cli/index.ts +420 -0
- package/src/config.ts +189 -0
- package/src/exit-codes.ts +20 -0
- package/src/index.tsx +35 -0
- package/src/js-coverage.ts +213 -0
- package/src/node.ts +52 -0
- package/src/path-rewrite.ts +52 -0
- package/src/process-ios-native-coverage.ts +425 -0
- package/src/pull-native-coverage.ts +269 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CoverageConfig } from './config.js';
|
|
2
|
+
export type PullOptions = {
|
|
3
|
+
softFail?: boolean;
|
|
4
|
+
outputDir?: string;
|
|
5
|
+
config?: CoverageConfig;
|
|
6
|
+
};
|
|
7
|
+
export declare function getAdbBinary(): string;
|
|
8
|
+
export declare function resolveAndroidDeviceId(preferredDeviceId?: string): string;
|
|
9
|
+
export declare function androidCoverageFileExists(deviceId: string, config?: CoverageConfig): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Pull Android Emma/Jacoco `.ec` from the device into `outputDir`.
|
|
12
|
+
* Ported from RNFB tests/scripts/pull-native-coverage.js with config injection.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pullAndroidCoverage(deviceId: string, options?: PullOptions): string | null;
|
|
15
|
+
export declare function pullAndroidCoverageWithRetry(deviceId: string, options?: PullOptions & {
|
|
16
|
+
retries?: number;
|
|
17
|
+
intervalMs?: number;
|
|
18
|
+
}): Promise<string | null>;
|
|
19
|
+
/**
|
|
20
|
+
* Pull iOS LLVM `.profraw` files from a simulator app container.
|
|
21
|
+
* Ported from RNFB with config-driven bundle id / output paths.
|
|
22
|
+
*/
|
|
23
|
+
export declare function pullIosCoverage(deviceId: string, options?: PullOptions): string[];
|
|
24
|
+
export type AndroidReportOptions = {
|
|
25
|
+
androidDir?: string;
|
|
26
|
+
jacocoXml?: string;
|
|
27
|
+
config?: CoverageConfig;
|
|
28
|
+
/** When true (default), assert Jacoco XML after a successful Gradle report. */
|
|
29
|
+
assertAfterReport?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Run a consumer Jacoco report Gradle task, then optionally assert LINE hits.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runJacocoTestReport(options?: AndroidReportOptions | string): {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
exitCode: number;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=pull-native-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull-native-coverage.d.ts","sourceRoot":"","sources":["../../../src/pull-native-coverage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAI/C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED,wBAAgB,sBAAsB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBzE;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,cAAwC,GAC/C,OAAO,CAcT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,MAAM,GAAG,IAAI,CAqCf;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACpE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoCxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,MAAM,EAAE,CAwDV;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,oBAAoB,GAAG,MAAW,GAC1C;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAuCnC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-coverage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Native code coverage tooling for React Native (TurboModule + CLI + Expo config plugin)",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"react-native": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./node": {
|
|
14
|
+
"types": "./lib/typescript/src/node.d.ts",
|
|
15
|
+
"default": "./lib/module/node.js"
|
|
16
|
+
},
|
|
17
|
+
"./app.plugin.js": "./app.plugin.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"rn-coverage": "./bin/rn-coverage.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"lib",
|
|
26
|
+
"android",
|
|
27
|
+
"ios",
|
|
28
|
+
"bin",
|
|
29
|
+
"plugin/build",
|
|
30
|
+
"app.plugin.js",
|
|
31
|
+
"cocoapods",
|
|
32
|
+
"docs",
|
|
33
|
+
"docs.json",
|
|
34
|
+
"react-native-coverage.config.js.example",
|
|
35
|
+
"*.podspec",
|
|
36
|
+
"react-native.config.js",
|
|
37
|
+
"!ios/build",
|
|
38
|
+
"!android/build",
|
|
39
|
+
"!android/gradle",
|
|
40
|
+
"!android/gradlew",
|
|
41
|
+
"!android/gradlew.bat",
|
|
42
|
+
"!android/local.properties",
|
|
43
|
+
"!**/__tests__",
|
|
44
|
+
"!**/__fixtures__",
|
|
45
|
+
"!**/__mocks__",
|
|
46
|
+
"!**/.*"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"example": "yarn workspace react-native-coverage-example",
|
|
50
|
+
"example:dynamic": "yarn workspace react-native-coverage-example-dynamic",
|
|
51
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build example-dynamic/ios/build lib plugin/build",
|
|
52
|
+
"prepare": "bob build && yarn build:plugin",
|
|
53
|
+
"build:plugin": "tsc --project plugin/tsconfig.json",
|
|
54
|
+
"typecheck": "tsc",
|
|
55
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
56
|
+
"test": "jest",
|
|
57
|
+
"test:coverage": "jest --coverage",
|
|
58
|
+
"e2e:ios:dynamic": "CELL=dynamic bash scripts/ci/run-ios-e2e-cell.sh",
|
|
59
|
+
"e2e:ios:static": "CELL=static bash scripts/ci/run-ios-e2e-cell.sh",
|
|
60
|
+
"e2e:android": "bash scripts/ci/run-android-e2e.sh",
|
|
61
|
+
"commitlint": "commitlint",
|
|
62
|
+
"release": "semantic-release",
|
|
63
|
+
"release:dry-run": "semantic-release --dry-run"
|
|
64
|
+
},
|
|
65
|
+
"keywords": [
|
|
66
|
+
"react-native",
|
|
67
|
+
"coverage",
|
|
68
|
+
"jacoco",
|
|
69
|
+
"llvm",
|
|
70
|
+
"istanbul",
|
|
71
|
+
"nyc",
|
|
72
|
+
"ios",
|
|
73
|
+
"android",
|
|
74
|
+
"turbo-module"
|
|
75
|
+
],
|
|
76
|
+
"repository": {
|
|
77
|
+
"type": "git",
|
|
78
|
+
"url": "git+https://github.com/invertase/react-native-coverage.git"
|
|
79
|
+
},
|
|
80
|
+
"author": "Invertase <oss@invertase.io> (https://invertase.io)",
|
|
81
|
+
"license": "Apache-2.0",
|
|
82
|
+
"bugs": {
|
|
83
|
+
"url": "https://github.com/invertase/react-native-coverage/issues"
|
|
84
|
+
},
|
|
85
|
+
"homepage": "https://github.com/invertase/react-native-coverage#readme",
|
|
86
|
+
"publishConfig": {
|
|
87
|
+
"registry": "https://registry.npmjs.org/"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"commander": "^13.1.0",
|
|
91
|
+
"nyc": "^17.1.0"
|
|
92
|
+
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"@commitlint/cli": "^19.8.1",
|
|
95
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
96
|
+
"@eslint/compat": "^2.1.0",
|
|
97
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
98
|
+
"@eslint/js": "^9.39.4",
|
|
99
|
+
"@react-native/babel-preset": "0.86.3",
|
|
100
|
+
"@react-native/eslint-config": "0.86.3",
|
|
101
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
102
|
+
"@semantic-release/github": "^12.0.0",
|
|
103
|
+
"@semantic-release/npm": "^13.1.1",
|
|
104
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
105
|
+
"@types/jest": "^29.5.14",
|
|
106
|
+
"@types/react": "^19.2.0",
|
|
107
|
+
"babel-plugin-istanbul": "^7.0.1",
|
|
108
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
109
|
+
"del-cli": "^7.0.0",
|
|
110
|
+
"eslint": "^9.39.4",
|
|
111
|
+
"eslint-config-prettier": "^10.1.8",
|
|
112
|
+
"eslint-plugin-ft-flow": "^3.0.11",
|
|
113
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
114
|
+
"expo": "~57.0.18",
|
|
115
|
+
"jest": "^29.7.0",
|
|
116
|
+
"prettier": "^3.8.3",
|
|
117
|
+
"react": "19.2.3",
|
|
118
|
+
"react-native": "0.86.3",
|
|
119
|
+
"react-native-builder-bob": "^0.43.0",
|
|
120
|
+
"semantic-release": "^25.0.1",
|
|
121
|
+
"ts-jest": "^29.4.0",
|
|
122
|
+
"turbo": "^2.9.16",
|
|
123
|
+
"typescript": "^5.9.2"
|
|
124
|
+
},
|
|
125
|
+
"peerDependencies": {
|
|
126
|
+
"expo": ">=52.0.0",
|
|
127
|
+
"react": "*",
|
|
128
|
+
"react-native": "*"
|
|
129
|
+
},
|
|
130
|
+
"peerDependenciesMeta": {
|
|
131
|
+
"expo": {
|
|
132
|
+
"optional": true
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"workspaces": [
|
|
136
|
+
"example",
|
|
137
|
+
"example/fixture-lib",
|
|
138
|
+
"example-dynamic",
|
|
139
|
+
"e2e"
|
|
140
|
+
],
|
|
141
|
+
"packageManager": "yarn@4.11.0",
|
|
142
|
+
"react-native-builder-bob": {
|
|
143
|
+
"source": "src",
|
|
144
|
+
"output": "lib",
|
|
145
|
+
"targets": [
|
|
146
|
+
[
|
|
147
|
+
"module",
|
|
148
|
+
{
|
|
149
|
+
"esm": true
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
"typescript",
|
|
154
|
+
{
|
|
155
|
+
"project": "tsconfig.build.json"
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
"codegenConfig": {
|
|
161
|
+
"name": "CoverageSpec",
|
|
162
|
+
"type": "modules",
|
|
163
|
+
"jsSrcsDir": "src",
|
|
164
|
+
"android": {
|
|
165
|
+
"javaPackageName": "com.coverage"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"prettier": {
|
|
169
|
+
"quoteProps": "consistent",
|
|
170
|
+
"singleQuote": true,
|
|
171
|
+
"tabWidth": 2,
|
|
172
|
+
"trailingComma": "es5",
|
|
173
|
+
"useTabs": false
|
|
174
|
+
},
|
|
175
|
+
"create-react-native-library": {
|
|
176
|
+
"type": "turbo-module",
|
|
177
|
+
"languages": "kotlin-objc",
|
|
178
|
+
"tools": [
|
|
179
|
+
"eslint",
|
|
180
|
+
"jest"
|
|
181
|
+
],
|
|
182
|
+
"version": "0.63.0"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
+
export type ReactNativeCoveragePluginProps = {
|
|
3
|
+
/** iOS Mach-O / framework basename prefixes for multi-image LINKEDIT flush. */
|
|
4
|
+
frameworkNamePrefixes?: string[];
|
|
5
|
+
/** Android Gradle project-name substrings to instrument + report. */
|
|
6
|
+
libraryProjectMatchers?: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Restore Coverage / fixture pods as dynamic frameworks after Expo's
|
|
9
|
+
* React-Core staticlib downgrade.
|
|
10
|
+
*
|
|
11
|
+
* Default **false** for Expo: Expo forces React-Core (and many RN pods) to
|
|
12
|
+
* static libraries, so dynamic Coverage* pods fail CocoaPods' transitive
|
|
13
|
+
* static→dynamic check. Set true on bare RN / RNFB hosts that build React
|
|
14
|
+
* as dynamic frameworks (primary multi-image LINKEDIT cell).
|
|
15
|
+
*/
|
|
16
|
+
forceDynamicFrameworks?: boolean;
|
|
17
|
+
/** Wire Gradle helpers + testCoverageEnabled. Default true. */
|
|
18
|
+
enableAndroidCoverage?: boolean;
|
|
19
|
+
};
|
|
20
|
+
declare const _default: ConfigPlugin<void | ReactNativeCoveragePluginProps>;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const PACKAGE_NAME = 'react-native-coverage';
|
|
10
|
+
/** Bump when Podfile injection shape changes so re-prebuild rewrites. */
|
|
11
|
+
const PODFILE_MARKER = '# react-native-coverage: post_install coverage flags';
|
|
12
|
+
const ROOT_GRADLE_MARKER = '// react-native-coverage: root instrumentation';
|
|
13
|
+
const APP_GRADLE_MARKER = '// react-native-coverage: app jacoco report';
|
|
14
|
+
const APP_DEBUG_MARKER = '// react-native-coverage: enable testCoverageEnabled in debug builds';
|
|
15
|
+
const DEFAULT_PREFIXES = ['CoverageFixture'];
|
|
16
|
+
const DEFAULT_MATCHERS = ['coverage-fixture', 'react-native-coverage'];
|
|
17
|
+
function resolvePackageRootGroovy() {
|
|
18
|
+
return ('new File(["node", "--print", ' +
|
|
19
|
+
`"require.resolve('${PACKAGE_NAME}/package.json')"` +
|
|
20
|
+
'].execute(null, rootDir).text.trim()).getParentFile()');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Expo config plugin — safe split:
|
|
24
|
+
* - Android: root instrumentation helper + app Jacoco report + testCoverageEnabled
|
|
25
|
+
* - iOS: dynamic frameworks (Podfile.properties) + Ruby helper require/call
|
|
26
|
+
* - Pod LLVM flags + dynamic-framework restore: shipped Ruby helper (not regex)
|
|
27
|
+
*/
|
|
28
|
+
const withReactNativeCoverage = (config, props) => {
|
|
29
|
+
const frameworkNamePrefixes = props?.frameworkNamePrefixes?.length
|
|
30
|
+
? props.frameworkNamePrefixes
|
|
31
|
+
: DEFAULT_PREFIXES;
|
|
32
|
+
const libraryProjectMatchers = props?.libraryProjectMatchers?.length
|
|
33
|
+
? props.libraryProjectMatchers
|
|
34
|
+
: DEFAULT_MATCHERS;
|
|
35
|
+
const forceDynamicFrameworks = props?.forceDynamicFrameworks === true;
|
|
36
|
+
const enableAndroidCoverage = props?.enableAndroidCoverage !== false;
|
|
37
|
+
// Primary proof cell is iOS dynamic frameworks (mode-c LINKEDIT).
|
|
38
|
+
config = (0, config_plugins_1.withPodfileProperties)(config, (cfg) => {
|
|
39
|
+
cfg.modResults['ios.useFrameworks'] = 'dynamic';
|
|
40
|
+
return cfg;
|
|
41
|
+
});
|
|
42
|
+
if (enableAndroidCoverage) {
|
|
43
|
+
config = (0, config_plugins_1.withProjectBuildGradle)(config, (cfg) => {
|
|
44
|
+
if (cfg.modResults.language !== 'groovy') {
|
|
45
|
+
return cfg;
|
|
46
|
+
}
|
|
47
|
+
if (cfg.modResults.contents.includes(ROOT_GRADLE_MARKER)) {
|
|
48
|
+
return cfg;
|
|
49
|
+
}
|
|
50
|
+
const matchersLiteral = libraryProjectMatchers
|
|
51
|
+
.map((m) => `'${m.replace(/'/g, "\\'")}'`)
|
|
52
|
+
.join(', ');
|
|
53
|
+
const snippet = `
|
|
54
|
+
${ROOT_GRADLE_MARKER}
|
|
55
|
+
ext.coverageLibraryProjectMatchers = [${matchersLiteral}]
|
|
56
|
+
def rnCoverageRoot = ${resolvePackageRootGroovy()}
|
|
57
|
+
apply from: new File(rnCoverageRoot, "android/rn-coverage.gradle")
|
|
58
|
+
`;
|
|
59
|
+
cfg.modResults.contents = `${cfg.modResults.contents.trimEnd()}\n${snippet}\n`;
|
|
60
|
+
return cfg;
|
|
61
|
+
});
|
|
62
|
+
config = (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => {
|
|
63
|
+
if (cfg.modResults.language !== 'groovy') {
|
|
64
|
+
return cfg;
|
|
65
|
+
}
|
|
66
|
+
if (!cfg.modResults.contents.includes(APP_DEBUG_MARKER)) {
|
|
67
|
+
// Prefer buildTypes.debug — do NOT match signingConfigs.debug.
|
|
68
|
+
if (/buildTypes\s*\{[\s\S]*?\bdebug\s*\{/.test(cfg.modResults.contents)) {
|
|
69
|
+
cfg.modResults.contents = cfg.modResults.contents.replace(/(buildTypes\s*\{[\s\S]*?\bdebug\s*\{)/, `$1\n testCoverageEnabled true\n ${APP_DEBUG_MARKER}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
cfg.modResults.contents += `\n${APP_DEBUG_MARKER}\n`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!cfg.modResults.contents.includes(APP_GRADLE_MARKER)) {
|
|
76
|
+
const snippet = `
|
|
77
|
+
${APP_GRADLE_MARKER}
|
|
78
|
+
def rnCoverageRoot = ${resolvePackageRootGroovy()}
|
|
79
|
+
apply from: new File(rnCoverageRoot, "android/rn-coverage-jacoco.gradle")
|
|
80
|
+
`;
|
|
81
|
+
cfg.modResults.contents = `${cfg.modResults.contents.trimEnd()}\n${snippet}\n`;
|
|
82
|
+
}
|
|
83
|
+
return cfg;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
config = (0, config_plugins_1.withDangerousMod)(config, [
|
|
87
|
+
'ios',
|
|
88
|
+
async (cfg) => {
|
|
89
|
+
const podfilePath = node_path_1.default.join(cfg.modRequest.platformProjectRoot, 'Podfile');
|
|
90
|
+
if (!node_fs_1.default.existsSync(podfilePath)) {
|
|
91
|
+
return cfg;
|
|
92
|
+
}
|
|
93
|
+
let contents = node_fs_1.default.readFileSync(podfilePath, 'utf8');
|
|
94
|
+
// Skip only when the full helper shape is already present.
|
|
95
|
+
if (contents.includes(PODFILE_MARKER) &&
|
|
96
|
+
contents.includes('force_dynamic_frameworks:') &&
|
|
97
|
+
contents.includes('install_installer_hooks!')) {
|
|
98
|
+
return cfg;
|
|
99
|
+
}
|
|
100
|
+
const requireLine = "require_relative '../node_modules/react-native-coverage/cocoapods/coverage_post_install'\n";
|
|
101
|
+
if (!contents.includes('coverage_post_install')) {
|
|
102
|
+
contents = requireLine + contents;
|
|
103
|
+
}
|
|
104
|
+
// Strip older helper injection so we can rewrite cleanly.
|
|
105
|
+
contents = contents.replace(/\n?[ \t]*# react-native-coverage: post_install coverage flags(?: \(05C4\))?\n[ \t]*ReactNativeCoverage\.apply_post_install!\([\s\S]*?\)\n?/g, '\n');
|
|
106
|
+
contents = contents.replace(/\n?[ \t]*ReactNativeCoverage\.install_installer_hooks!\n?/g, '\n');
|
|
107
|
+
contents = contents.replace(/\n?[ \t]*ReactNativeCoverage\.install_installer_hooks!\([\s\S]*?\)\n?/g, '\n');
|
|
108
|
+
const prefixesRuby = frameworkNamePrefixes
|
|
109
|
+
.map((p) => `'${p.replace(/'/g, "\\'")}'`)
|
|
110
|
+
.join(', ');
|
|
111
|
+
const hooksLine = `
|
|
112
|
+
# react-native-coverage: wrap Installer after Expo use_expo_modules! patch
|
|
113
|
+
ReactNativeCoverage.install_installer_hooks!(
|
|
114
|
+
force_dynamic_frameworks: ${forceDynamicFrameworks}
|
|
115
|
+
)
|
|
116
|
+
`;
|
|
117
|
+
// Must run AFTER react_native_post_install so RN does not wipe OTHER_CFLAGS.
|
|
118
|
+
const helperCall = `
|
|
119
|
+
${PODFILE_MARKER}
|
|
120
|
+
ReactNativeCoverage.apply_post_install!(
|
|
121
|
+
installer,
|
|
122
|
+
framework_name_prefixes: [${prefixesRuby}],
|
|
123
|
+
force_dynamic_frameworks: ${forceDynamicFrameworks}
|
|
124
|
+
)
|
|
125
|
+
`;
|
|
126
|
+
if (!contents.includes('install_installer_hooks!')) {
|
|
127
|
+
if (contents.includes('post_install do |installer|')) {
|
|
128
|
+
contents = contents.replace(/post_install do \|installer\|/, `${hooksLine}post_install do |installer|`);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
contents += `\n${hooksLine}\n`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (contents.includes('react_native_post_install(')) {
|
|
135
|
+
contents = contents.replace(/(react_native_post_install\([\s\S]*?\))\n/, `$1\n${helperCall}`);
|
|
136
|
+
}
|
|
137
|
+
else if (contents.includes('post_install do |installer|')) {
|
|
138
|
+
contents = contents.replace(/post_install do \|installer\|/, `post_install do |installer|${helperCall}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
contents += `
|
|
142
|
+
post_install do |installer|
|
|
143
|
+
${helperCall}
|
|
144
|
+
end
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
node_fs_1.default.writeFileSync(podfilePath, contents);
|
|
148
|
+
return cfg;
|
|
149
|
+
},
|
|
150
|
+
]);
|
|
151
|
+
return config;
|
|
152
|
+
};
|
|
153
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withReactNativeCoverage, PACKAGE_NAME, '0.1.0');
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example consumer config — copy to react-native-coverage.config.js
|
|
3
|
+
* and adjust for your dedicated test app (Pattern C).
|
|
4
|
+
*
|
|
5
|
+
* Defaults are intentionally product-neutral.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = {
|
|
8
|
+
nativeModuleName: 'Coverage',
|
|
9
|
+
app: {
|
|
10
|
+
androidApplicationId: 'com.example.coverage',
|
|
11
|
+
iosBundleId: 'com.example.coverage',
|
|
12
|
+
iosProductName: 'CoverageExample',
|
|
13
|
+
},
|
|
14
|
+
ios: {
|
|
15
|
+
// e.g. ['MyLib'] to include MyLib*.framework as llvm-cov objects
|
|
16
|
+
frameworkNamePrefixes: [],
|
|
17
|
+
},
|
|
18
|
+
android: {
|
|
19
|
+
libraryProjectMatchers: [],
|
|
20
|
+
detoxStagingPath: '/data/local/tmp/coverage/coverage.ec',
|
|
21
|
+
coverageRelativePath: 'files/coverage.ec',
|
|
22
|
+
jacocoReportXml:
|
|
23
|
+
'android/app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml',
|
|
24
|
+
},
|
|
25
|
+
sourcePathRewrite: [
|
|
26
|
+
{ kind: 'after-marker', marker: '/packages/', includeMarker: true },
|
|
27
|
+
{
|
|
28
|
+
kind: 'regex',
|
|
29
|
+
pattern: '^.*/@example-scope/([^/]+)/(.+)$',
|
|
30
|
+
replacement: 'packages/$1/$2',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
/**
|
|
34
|
+
* Prefer true in CI: pull/export/report/assert exit 2 when expected
|
|
35
|
+
* hits or artifacts are empty.
|
|
36
|
+
*/
|
|
37
|
+
strict: true,
|
|
38
|
+
assert: {
|
|
39
|
+
/** LCOV SF: paths must include at least one of these substrings. */
|
|
40
|
+
lcovPathIncludes: ['packages/'],
|
|
41
|
+
/** Jacoco <package name> substrings that must have LINE covered > 0. */
|
|
42
|
+
jacocoPackageIncludes: [],
|
|
43
|
+
defaultLcovPath: 'coverage/ios/lcov.info',
|
|
44
|
+
defaultJacocoXmlPath:
|
|
45
|
+
'android/app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TurboModule surface for native coverage flush (New Architecture only).
|
|
5
|
+
*/
|
|
6
|
+
export interface Spec extends TurboModule {
|
|
7
|
+
flush(): void;
|
|
8
|
+
/**
|
|
9
|
+
* Persist Istanbul `global.__coverage__` JSON to the app sandbox
|
|
10
|
+
* (`files/coverage-final.json` on Android, `Documents/coverage-final.json` on iOS)
|
|
11
|
+
* for host-side `rn-coverage js pull` + NYC remap.
|
|
12
|
+
*/
|
|
13
|
+
dumpJsCoverage(json: string): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('Coverage');
|