Skip to content

Commit 7464130

Browse files
authored
Merge pull request #316 from recca0120/fix/phpunit10
Fix/phpunit10
2 parents e1e20b1 + 6e7811d commit 7464130

File tree

8 files changed

+164
-136
lines changed

8 files changed

+164
-136
lines changed

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"displayName": "PHPUnit Test Explorer",
55
"icon": "img/icon.png",
66
"publisher": "recca0120",
7-
"version": "3.7.4",
7+
"version": "3.7.5",
88
"private": true,
99
"license": "MIT",
1010
"repository": {
@@ -192,7 +192,7 @@
192192
"@vscode/vsce": "^3.3.2",
193193
"chai": "^5.2.0",
194194
"eslint": "^9.26.0",
195-
"fast-xml-parser": "^5.2.2",
195+
"fast-xml-parser": "^5.2.3",
196196
"glob": "^11.0.2",
197197
"minimatch": "^10.0.1",
198198
"mocha": "^11.2.2",

src/PHPUnit/ProblemMatcher/PHPUnitProblemMatcher.test.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,33 @@ describe('PHPUnit ProblemMatcher Text', () => {
236236
);
237237
});
238238

239-
fit('fix PHPUnit 10 without details', () => {
239+
it('fix PHPUnit10 testFailed', () => {
240+
const contents = [
241+
`##teamcity[testSuiteStarted name='App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest' locationHint='php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest' flowId='5161']`,
242+
`##teamcity[testFailed name='testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled' message='Error: Class "App\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer" not found' details='/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php:28|n' duration='0' flowId='5161']`,
243+
`##teamcity[testSuiteFinished name='App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest' flowId='5161']`,
244+
];
245+
246+
problemMatcher.parse(contents[0]);
247+
expect(problemMatcher.parse(contents[1])).toEqual(
248+
expect.objectContaining({
249+
event: TeamcityEvent.testFailed,
250+
name: 'testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
251+
// locationHint: 'php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
252+
flowId: 5161,
253+
id: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
254+
file: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php',
255+
message: 'Error: Class "App\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer" not found',
256+
details: [{
257+
file: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php',
258+
line: 28,
259+
}],
260+
duration: 0,
261+
}),
262+
);
263+
});
264+
265+
it('fix PHPUnit10 testIgnored', () => {
240266
const contents = [
241267
`##teamcity[testSuiteStarted name='Tests\\Feature\\ChatControllerTest' locationHint='php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest' flowId='22946']`,
242268
`##teamcity[testIgnored name='test_permission' message='ChatControllerTest uses PlayerService' duration='0' flowId='22946']`,
@@ -251,7 +277,7 @@ describe('PHPUnit ProblemMatcher Text', () => {
251277
expect.objectContaining({
252278
event: TeamcityEvent.testIgnored,
253279
name: 'test_permission',
254-
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::test_permission',
280+
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_permission',
255281
flowId: 22946,
256282
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_permission',
257283
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
@@ -268,7 +294,7 @@ describe('PHPUnit ProblemMatcher Text', () => {
268294
expect.objectContaining({
269295
event: TeamcityEvent.testIgnored,
270296
name: 'test_grant_chat_token',
271-
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::test_grant_chat_token',
297+
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_grant_chat_token',
272298
flowId: 22946,
273299
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_grant_chat_token',
274300
file: '/var/www/html/tests/Feature/ChatControllerTest.php',

src/PHPUnit/Transformer/Fixer.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { TeamcityEvent, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';
2+
import { capitalize } from '../utils';
3+
4+
export class PestV1Fixer {
5+
static fixLocationHint(locationHint: string) {
6+
return this.fixDataSet(/^tests\//.test(locationHint) ? locationHint : locationHint.substring(locationHint.lastIndexOf('tests/')));
7+
}
8+
9+
static fixFlowId(results = new Map<string, TestResult>(), testResult?: TestResult) {
10+
if (!testResult) {
11+
return testResult;
12+
}
13+
14+
const events = [TeamcityEvent.testStarted, TeamcityEvent.testFailed, TeamcityEvent.testIgnored];
15+
if ('event' in testResult && !events.includes(testResult.event) || (testResult as any).flowId) {
16+
return testResult;
17+
}
18+
19+
const result = Array.from(results.values()).reverse().find((result: TestResult) => {
20+
if (testResult.event !== TeamcityEvent.testStarted) {
21+
return result.event === TeamcityEvent.testStarted && (result as any).name === (testResult as any).name;
22+
}
23+
24+
const matched = (testResult as any).id?.match(/\((?<id>.+)\)/);
25+
26+
return matched && (result as any).id === matched.groups?.id.replace(/\\/g, '/') + 'Test';
27+
});
28+
29+
(testResult as any).flowId = (result as any)?.flowId;
30+
31+
return testResult;
32+
}
33+
34+
private static fixDataSet(locationHint: string) {
35+
const matched = locationHint.match(/(?<description>.+)\swith\s\('(?<data>.+)'\)/);
36+
37+
return matched && matched.groups?.description
38+
? `${matched.groups.description} with data set "(\'${matched.groups.data}\')"`
39+
: locationHint;
40+
}
41+
}
42+
43+
export class Str {
44+
static prefix = '__pest_evaluable_';
45+
46+
static evaluable(code: string) {
47+
return this.prefix + code.replace(/_/g, '__').replace(/\s/g, '_').replace(/[^a-zA-Z0-9_\u0080-\uFFFF]/g, '_');
48+
}
49+
}
50+
51+
export class PestV2Fixer {
52+
static fixId(location: string, name: string) {
53+
return this.hasPrefix(name) ? name : location;
54+
}
55+
56+
static isEqualsPestV2DataSetId(result: TestResult, testItemId: string) {
57+
if (!('id' in result) || !this.hasPrefix(result.id)) {
58+
return false;
59+
}
60+
61+
let [classFQN, method] = testItemId.split('::');
62+
classFQN = capitalize(classFQN.replace(/\//g, '\\').replace(/\.php$/, ''));
63+
64+
return [classFQN, this.methodName(method)].join('::') === result.id;
65+
}
66+
67+
private static hasPrefix(id?: string) {
68+
return id && new RegExp(Str.prefix).test(id);
69+
}
70+
71+
static methodName(methodName: string) {
72+
methodName = methodName.replace(/\{@\*}/g, '*/');
73+
const matched = methodName.match(/(?<method>.*)\swith\sdata\sset\s(?<dataset>.+)/);
74+
let dataset = '';
75+
if (matched) {
76+
methodName = matched.groups!.method;
77+
dataset = matched.groups!.dataset.replace(/\|'/g, '\'');
78+
}
79+
80+
return Str.evaluable(methodName) + dataset;
81+
}
82+
}
83+
84+
export class PHPUnitFixer {
85+
static fixDetails(results = new Map<string, TestResult>(), testResult: TestResult & {
86+
name: string,
87+
locationHint?: string,
88+
file?: string,
89+
details?: Array<{ file: string, line: number }>,
90+
}) {
91+
if (testResult.details && testResult.file) {
92+
return testResult;
93+
}
94+
95+
const result = Array.from(results.values()).reverse().find((result) => {
96+
return [TeamcityEvent.testSuiteStarted, TeamcityEvent.testStarted].includes(result.event);
97+
}) as (TestSuiteStarted | TestStarted | undefined);
98+
99+
if (!result) {
100+
return testResult;
101+
}
102+
103+
const file = result.file!;
104+
if (!testResult.file) {
105+
testResult.file = file;
106+
}
107+
108+
if (!testResult.details) {
109+
testResult.details = [{ file: file, line: 1 }];
110+
}
111+
112+
if (!testResult.locationHint) {
113+
const locationHint = result.locationHint?.split('::').slice(0, 2).join('::');
114+
testResult.locationHint = [locationHint, testResult.name]
115+
.filter(value => !!value)
116+
.join('::');
117+
}
118+
119+
return testResult;
120+
}
121+
}

src/PHPUnit/Transformer/PHPUnitTransformer.ts

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,7 @@
1-
import { TeamcityEvent, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';
21
import { TestDefinition, TestType } from '../types';
32
import { capitalize, snakeCase, titleCase } from '../utils';
43
import { Transformer } from './Transformer';
54

6-
export class PHPUnitFixer {
7-
static fixDetails(results = new Map<string, TestResult>(), testResult: TestResult & {
8-
name: string,
9-
locationHint?: string,
10-
file?: string,
11-
details?: Array<{ file: string, line: number }>,
12-
}) {
13-
if (testResult.details && testResult.file) {
14-
return testResult;
15-
}
16-
17-
const result = Array.from(results.values()).reverse().find((result) => {
18-
return [TeamcityEvent.testSuiteStarted, TeamcityEvent.testStarted].includes(result.event);
19-
}) as (TestSuiteStarted | TestStarted | undefined);
20-
21-
if (!result) {
22-
return testResult;
23-
}
24-
25-
const file = result.file!;
26-
if (!testResult.file) {
27-
testResult.file = file;
28-
}
29-
30-
if (!testResult.details) {
31-
testResult.details = [{ file: file, line: 1 }];
32-
}
33-
34-
if (!testResult.locationHint) {
35-
const locationHint = result.locationHint?.split('::').slice(0, 1).join('::');
36-
testResult.locationHint = [locationHint, testResult.name]
37-
.filter(value => !!value)
38-
.join('::');
39-
}
40-
41-
return testResult;
42-
}
43-
}
44-
455
export class PHPUnitTransformer extends Transformer {
466
uniqueId(testDefinition: Pick<TestDefinition, 'type' | 'classFQN' | 'methodName' | 'annotations'>): string {
477
let { type, classFQN } = testDefinition;

src/PHPUnit/Transformer/PestTransFormer.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TestType } from '../types';
2-
import { PestTransformer, PestV2Fixer } from './PestTransformer';
2+
import { PestV2Fixer } from './Fixer';
3+
import { PestTransformer } from './PestTransformer';
34

45
describe('PestTransformer', () => {
56
const transformer = new PestTransformer();

src/PHPUnit/Transformer/PestTransformer.ts

Lines changed: 2 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,9 @@
1-
import { TeamcityEvent, TestResult } from '../ProblemMatcher';
21
import { TestDefinition, TestType } from '../types';
3-
import { capitalize, uncapitalize } from '../utils';
2+
import { uncapitalize } from '../utils';
3+
import { PestV1Fixer, PestV2Fixer } from './Fixer';
44
import { PHPUnitTransformer } from './PHPUnitTransformer';
55
import { TransformerFactory } from './TransformerFactory';
66

7-
8-
export class Str {
9-
static prefix = '__pest_evaluable_';
10-
11-
static evaluable(code: string) {
12-
return this.prefix + code.replace(/_/g, '__').replace(/\s/g, '_').replace(/[^a-zA-Z0-9_\u0080-\uFFFF]/g, '_');
13-
}
14-
}
15-
16-
export class PestV1Fixer {
17-
static fixLocationHint(locationHint: string) {
18-
return this.fixDataSet(/^tests\//.test(locationHint) ? locationHint : locationHint.substring(locationHint.lastIndexOf('tests/')));
19-
}
20-
21-
static fixFlowId(results = new Map<string, TestResult>(), testResult?: TestResult) {
22-
if (!testResult) {
23-
return testResult;
24-
}
25-
26-
const events = [TeamcityEvent.testStarted, TeamcityEvent.testFailed, TeamcityEvent.testIgnored];
27-
if ('event' in testResult && !events.includes(testResult.event) || (testResult as any).flowId) {
28-
return testResult;
29-
}
30-
31-
const result = Array.from(results.values()).reverse().find((result: TestResult) => {
32-
if (testResult.event !== TeamcityEvent.testStarted) {
33-
return result.event === TeamcityEvent.testStarted && (result as any).name === (testResult as any).name;
34-
}
35-
36-
const matched = (testResult as any).id?.match(/\((?<id>.+)\)/);
37-
38-
return matched && (result as any).id === matched.groups?.id.replace(/\\/g, '/') + 'Test';
39-
});
40-
41-
(testResult as any).flowId = (result as any)?.flowId;
42-
43-
return testResult;
44-
}
45-
46-
private static fixDataSet(locationHint: string) {
47-
const matched = locationHint.match(/(?<description>.+)\swith\s\('(?<data>.+)'\)/);
48-
49-
return matched && matched.groups?.description
50-
? `${matched.groups.description} with data set "(\'${matched.groups.data}\')"`
51-
: locationHint;
52-
}
53-
}
54-
55-
export class PestV2Fixer {
56-
static fixId(location: string, name: string) {
57-
return this.hasPrefix(name) ? name : location;
58-
}
59-
60-
static isEqualsPestV2DataSetId(result: TestResult, testItemId: string) {
61-
if (!('id' in result) || !this.hasPrefix(result.id)) {
62-
return false;
63-
}
64-
65-
let [classFQN, method] = testItemId.split('::');
66-
classFQN = capitalize(classFQN.replace(/\//g, '\\').replace(/\.php$/, ''));
67-
68-
return [classFQN, this.methodName(method)].join('::') === result.id;
69-
}
70-
71-
private static hasPrefix(id?: string) {
72-
return id && new RegExp(Str.prefix).test(id);
73-
}
74-
75-
static methodName(methodName: string) {
76-
methodName = methodName.replace(/\{@\*}/g, '*/');
77-
const matched = methodName.match(/(?<method>.*)\swith\sdata\sset\s(?<dataset>.+)/);
78-
let dataset = '';
79-
if (matched) {
80-
methodName = matched.groups!.method;
81-
dataset = matched.groups!.dataset.replace(/\|'/g, '\'');
82-
}
83-
84-
return Str.evaluable(methodName) + dataset;
85-
}
86-
}
87-
887
export class PestTransformer extends PHPUnitTransformer {
898
uniqueId(testDefinition: Pick<TestDefinition, 'type' | 'classFQN' | 'methodName' | 'annotations'>): string {
909
if (!TransformerFactory.isPest(testDefinition.classFQN!)) {

src/PHPUnit/Transformer/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
export * from './Fixer';
12
export * from './TransformerFactory';
23
export * from './Transformer';
34
export * from './PHPUnitTransformer';
4-
export * from './PestTransformer';
5+
export * from './PestTransformer';

0 commit comments

Comments
 (0)