Skip to content

Commit bf655d1

Browse files
authored
fix: add pcre regex support to coverage regex (#1587)
1 parent 1dc697c commit bf655d1

File tree

5 files changed

+122
-2
lines changed

5 files changed

+122
-2
lines changed

src/global.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@ import {RE2JS} from "re2js";
33
declare global {
44
interface String {
55
matchRE2JS(o: RE2JS): Array<string> | null;
6+
matchAllRE2JS(o: RE2JS): IterableIterator<RegExpMatchAll>;
67
}
78
}
9+
10+
interface RegExpMatchAll extends Array<string> {
11+
index?: number;
12+
input?: string;
13+
groups: Record<string, string> | undefined;
14+
}
15+
816
String.prototype.matchRE2JS = function (o: RE2JS): Array<string> | null {
917
let results: string[] | null = null;
1018
const matcher = o.matcher(this.toString());
@@ -16,3 +24,33 @@ String.prototype.matchRE2JS = function (o: RE2JS): Array<string> | null {
1624
}
1725
return results;
1826
};
27+
28+
String.prototype.matchAllRE2JS = function (o: RE2JS) {
29+
const self = this.toString();
30+
const matcher = o.matcher(self);
31+
32+
function* iterator (): IterableIterator<RegExpMatchAll> {
33+
while (matcher.find()) {
34+
const match: RegExpMatchAll = [] as unknown as RegExpMatchAll;
35+
for (let i = 0; i <= matcher.groupCount(); i++) {
36+
match.push(matcher.group(i));
37+
}
38+
match.index = matcher.groups[0];
39+
match.input = self;
40+
41+
if (matcher.namedGroups && Object.keys(matcher.namedGroups).length > 0) {
42+
const groups = Object.create(null);
43+
for (const [name, value] of Object.entries(matcher.namedGroups as Record<string, number>)) {
44+
groups[name] = matcher.group(value);
45+
}
46+
match.groups = groups;
47+
} else {
48+
match.groups = undefined;
49+
}
50+
51+
yield match;
52+
}
53+
}
54+
55+
return iterator();
56+
};

src/utils.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,13 @@ export class Utils {
8282
static async getCoveragePercent (cwd: string, stateDir: string, coverageRegex: string, jobName: string) {
8383
const content = await fs.readFile(`${cwd}/${stateDir}/output/${jobName}.log`, "utf8");
8484

85-
const regex = new RegExp(coverageRegex.replace(/^\//, "").replace(/\/$/, ""), "gm");
86-
const matches = Array.from(content.matchAll(regex));
85+
const regex = RE2JS.compile(
86+
coverageRegex
87+
.replace(/^\//, "")
88+
.replace(/\/$/, ""),
89+
RE2JS.MULTILINE,
90+
);
91+
const matches = Array.from(content.matchAllRE2JS(regex));
8792
if (matches.length === 0) return "0";
8893

8994
const lastMatch = matches[matches.length - 1];

tests/global.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import "../src/global";
2+
import {RE2JS} from "re2js";
3+
4+
const tests = [
5+
{
6+
paragraph: "hello world",
7+
regexp: "world",
8+
description: "basic string pattern",
9+
},
10+
{
11+
paragraph: "hello world",
12+
regexp: "foo",
13+
description: "no match found",
14+
},
15+
{
16+
paragraph: "foo bar foo baz foo",
17+
regexp: "foo",
18+
description: "multiple matches",
19+
},
20+
{
21+
paragraph: "abc123def456",
22+
regexp: "(\\d+)",
23+
description: "match with capturing group",
24+
},
25+
{
26+
paragraph: "color: #ff0000; background: #00ff00;",
27+
regexp: "#(?<hex>[0-9a-fA-F]{6})",
28+
description: "named capturing group",
29+
},
30+
{
31+
paragraph: "aaaa",
32+
regexp: "aa",
33+
description: "overlapping matches",
34+
},
35+
{
36+
paragraph: "",
37+
regexp: "a",
38+
description: "empty string",
39+
},
40+
{
41+
paragraph: "abc123abc",
42+
regexp: "^abc|abc$",
43+
description: "match at start or end",
44+
},
45+
{
46+
paragraph: "a.b*c+d?",
47+
regexp: "\\.",
48+
description: "special character dot",
49+
},
50+
];
51+
52+
describe("matchAllRE2JS should behave similarly to matchAll", () => {
53+
tests.forEach((t) => {
54+
test(t.description, () => {
55+
const matchAll = Array.from(t.paragraph.matchAll(new RegExp(t.regexp, "g")));
56+
const matchAllRE2JS = Array.from(t.paragraph.matchAllRE2JS(RE2JS.compile(t.regexp)));
57+
expect(matchAllRE2JS).toStrictEqual(matchAll);
58+
});
59+
});
60+
});

tests/test-cases/coverage/.gitlab-ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ import.meta.jest:
99
coverage: /All files.*?\|.*?\|.*?([\d\.]+)/
1010
script:
1111
- echo "All files | 99.3 | 97.91 | 100 | 100 | "
12+
13+
pcre regex:
14+
coverage: /(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/
15+
script:
16+
- echo "TOTAL 18 0 100%"

tests/test-cases/coverage/integration.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,15 @@ test("coverage <import.meta.jest>", async () => {
2929
const expected = [chalk`{black.bgGreenBright PASS } {blueBright import.meta.jest} 97.91% {gray coverage}`];
3030
expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected));
3131
});
32+
33+
test("coverage <pcre regex>", async () => {
34+
const writeStreams = new WriteStreamsMock();
35+
await handler({
36+
cwd: "tests/test-cases/coverage",
37+
job: ["pcre regex"],
38+
noColor: true,
39+
}, writeStreams);
40+
41+
const expected = [chalk`{black.bgGreenBright PASS } {blueBright pcre regex} 100% {gray coverage}`];
42+
expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining(expected));
43+
});

0 commit comments

Comments
 (0)