Skip to content

Commit

Permalink
refactor: various pest features
Browse files Browse the repository at this point in the history
  • Loading branch information
yaegassy committed Aug 8, 2023
1 parent 1d292d7 commit 0475151
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 299 deletions.
140 changes: 140 additions & 0 deletions src/__tests__/pest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { expect, test } from 'vitest';

import * as pestCommon from '../common/pest';
import * as phpParser from '../parsers/php/parser';
import * as testUtils from './testUtils';

test('Get pest test items', async () => {
const code = testUtils.stripInitialNewline(`
<?php
describe('dummy test', function () {
it('dummy of test 1', function () {
// ...
});
test('dummy of test 2', function () {
// ...
});
});
test('dummy of test 3', function () {
// ...
});
`);

const ast = phpParser.getAstByParseCode(code)!;

const testItems = pestCommon.getPestTestItems(ast);

const expected = [
{
description: 'dummy of test 1',
callName: 'it',
startOffset: 47,
endOffset: 103,
},
{
description: 'dummy of test 2',
callName: 'test',
startOffset: 109,
endOffset: 167,
},
{
description: 'dummy of test 3',
callName: 'test',
startOffset: 173,
endOffset: 223,
},
];

expect(testItems).toMatchObject(expected);
});

test('Get pest test description at editor offset', async () => {
const editorOffset = 125;

const testItems: pestCommon.PestTestItemType[] = [
{
description: 'dummy of test 1',
callName: 'it',
startOffset: 47,
endOffset: 103,
},
{
description: 'dummy of test 2',
callName: 'test',
startOffset: 109,
endOffset: 167,
},
];

const pestTestDecription = pestCommon.getPestTestDescriptionAtEditorOffset(testItems, editorOffset);

const expected = 'dummy of test 2';
expect(pestTestDecription).toBe(expected);
});

test('Get phpunit style test items', async () => {
// escape \ -> \\
const code = testUtils.stripInitialNewline(`
<?php
use PHPUnit\\Framework\\TestCase;
class PhpUnitStyleTest extends TestCase
{
public function testDummy1(): void
{
$expected = 'Dummy1';
$this->assertSame('Dummy2', $expected);
}
public function testDummy2(): void
{
$expected = 'Dummy2';
$this->assertSame('Dummy2', $expected);
}
}
`);

const ast = phpParser.getAstByParseCode(code)!;

const testItems = pestCommon.getPhpUnitStyleTestItems(ast);

const expected = [
{
methodName: 'testDummy1',
startOffset: 86,
endOffset: 210,
},
{
methodName: 'testDummy2',
startOffset: 216,
endOffset: 340,
},
];

expect(testItems).toMatchObject(expected);
});

test('Get phpunit style test name at editor offset', async () => {
const editorOffset = 220;

const testItems: pestCommon.PhpUnitTestItemType[] = [
{
methodName: 'testDummy1',
startOffset: 86,
endOffset: 210,
},
{
methodName: 'testDummy2',
startOffset: 216,
endOffset: 340,
},
];

const testName = pestCommon.getPhpUnitTestNameAtEditorOffset(testItems, editorOffset);

const expected = 'testDummy2';
expect(testName).toBe(expected);
});
7 changes: 7 additions & 0 deletions src/__tests__/testUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function printDebug(data: any) {
console.log(`==DEBUG==: ${JSON.stringify(data, null, 2)}`);
}

export function stripInitialNewline(text: string) {
return text.replace(/^\n/, '');
}
20 changes: 12 additions & 8 deletions src/commands/pest.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { commands, ExtensionContext, Terminal, Uri, window, workspace } from 'coc.nvim';

import path from 'path';
import fs from 'fs';
import * as pestParser from '../parsers/pest';
import path from 'path';
import * as pestCommon from '../common/pest';
import * as phpParser from '../parsers/php/parser';

let terminal: Terminal | undefined;

Expand Down Expand Up @@ -147,18 +148,21 @@ export function pestSingleTestCommand() {
return window.showErrorMessage('This file is not a PHP test file!');
}

const ast = pestParser.getAst(document.getText());
const editorOffset = document.offsetAt(position);

const ast = phpParser.getAstByParseCode(document.getText());
if (!ast) return;

const testItems = pestCommon.getPestTestItems(ast);
const pestTestName = pestCommon.getPestTestDescriptionAtEditorOffset(testItems, editorOffset);

let testName = '';
const pestTestDetails = await pestParser.getPestTestDetail(ast.children);
const pestTestName = pestParser.getTestNameFromPestTestDetails(pestTestDetails, position);
if (pestTestName) {
testName = pestTestName;
} else {
const methods = await pestParser.getMethods(ast.children);
const methodTestName = pestParser.getTestName(methods, position);
if (methodTestName) testName = methodTestName;
const phpUnitStyleTestItems = pestCommon.getPhpUnitStyleTestItems(ast);
const phpUnitTestName = pestCommon.getPhpUnitTestNameAtEditorOffset(phpUnitStyleTestItems, editorOffset);
if (phpUnitTestName) testName = phpUnitTestName;
}

if (testName) {
Expand Down
102 changes: 102 additions & 0 deletions src/common/pest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as phpParser from '../parsers/php/parser';

import { Call, Identifier, Method, Name as NameNode, Node, String as StringNode } from 'php-parser';

export type PestTestItemType = {
description: string;
callName: string;
startOffset: number;
endOffset: number;
};

export type PhpUnitTestItemType = {
methodName: string;
startOffset: number;
endOffset: number;
};

export function getPestTestItems(ast: Node) {
const items: PestTestItemType[] = [];

phpParser.walk((node, parent) => {
if (!parent) return;
if (parent.kind !== 'call') return;
if (node.kind !== 'name') return;
const parentCallNode = parent as Call;
const nameNode = node as NameNode;

if (!parentCallNode.loc) return;

if (!isPestTestName(nameNode.name)) return;
if (parentCallNode.arguments.length === 0) return;
if (parentCallNode.arguments[0].kind !== 'string') return;
const stringNode = parentCallNode.arguments[0] as StringNode;

items.push({
description: stringNode.value,
callName: nameNode.name,
startOffset: parentCallNode.loc.start.offset,
endOffset: parentCallNode.loc.end.offset,
});
}, ast);

return items;
}

export function isPestTestName(name: string) {
return ['test', 'it'].includes(name);
}

export function getPestTestDescriptionAtEditorOffset(testItems: PestTestItemType[], editorOffset: number) {
const testDescriptions: string[] = [];

for (const t of testItems) {
if (t.startOffset <= editorOffset && t.endOffset >= editorOffset) {
testDescriptions.push(t.description);
}
}

if (testDescriptions.length === 0) return undefined;
return testDescriptions[0];
}

export function getPhpUnitStyleTestItems(ast: Node) {
const items: PhpUnitTestItemType[] = [];

phpParser.walk((node, parent) => {
if (!parent) return;
if (parent.kind !== 'method') return;
if (node.kind !== 'identifier') return;
const parentMethodNode = parent as Method;
const identifierNode = node as Identifier;

if (!parentMethodNode.loc) return;

if (!isPhpUnitTestName(identifierNode.name)) return;

items.push({
methodName: identifierNode.name,
startOffset: parentMethodNode.loc.start.offset,
endOffset: parentMethodNode.loc.end.offset,
});
}, ast);

return items;
}

export function isPhpUnitTestName(name: string) {
return name.startsWith('test');
}

export function getPhpUnitTestNameAtEditorOffset(testItems: PhpUnitTestItemType[], editorOffset: number) {
const testNames: string[] = [];

for (const t of testItems) {
if (t.startOffset <= editorOffset && t.endOffset >= editorOffset) {
testNames.push(t.methodName);
}
}

if (testNames.length === 0) return undefined;
return testNames[0];
}
42 changes: 24 additions & 18 deletions src/lenses/pest.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import {
ExtensionContext,
languages,
CancellationToken,
CodeLens,
CodeLensProvider,
events,
ExtensionContext,
LinesTextDocument,
Position,
Range,
Uri,
events,
languages,
workspace,
} from 'coc.nvim';
import * as pestParser from '../parsers/pest';
import * as pestCommon from '../common/pest';
import * as phpParser from '../parsers/php/parser';

export function activate(context: ExtensionContext) {
if (!workspace.getConfiguration('intelephense').get<boolean>('client.disableCodeLens', false)) {
Expand All @@ -29,7 +30,7 @@ export function activate(context: ExtensionContext) {

export class PestCodeLensProvider implements CodeLensProvider {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async provideCodeLenses(document: LinesTextDocument, token: CancellationToken) {
async provideCodeLenses(document: LinesTextDocument, _token: CancellationToken) {
const filePath = Uri.parse(document.uri).fsPath;

if (document.languageId !== 'php' || !filePath.endsWith('Test.php')) {
Expand All @@ -44,16 +45,18 @@ export class PestCodeLensProvider implements CodeLensProvider {

// phpunit style
try {
const ast = pestParser.getAst(document.getText());
const ast = phpParser.getAstByParseCode(document.getText());
if (!ast) return;

const methods = await pestParser.getMethods(ast.children);
const testMethods = pestParser.getTestMethods(methods);
const phpUnitStyleTestItems = pestCommon.getPhpUnitStyleTestItems(ast);

if (phpUnitStyleTestItems.length > 0) {
for (const t of phpUnitStyleTestItems) {
const startPostion = document.positionAt(t.startOffset);
const endPostion = document.positionAt(t.endOffset);

testMethods.forEach((m) => {
if (m.startLine && m.endLine) {
const lens: CodeLens = {
range: Range.create(Position.create(m.startLine - 1, 0), Position.create(m.endLine, 0)),
range: Range.create(startPostion, endPostion),
command: {
title: codeLensTitle,
command: 'intelephense.pest.singleTest',
Expand All @@ -62,22 +65,25 @@ export class PestCodeLensProvider implements CodeLensProvider {

codeLenses.push(lens);
}
});
}
} catch (e) {
// noop
}

// pest style
try {
const ast = pestParser.getAst(document.getText());
const ast = phpParser.getAstByParseCode(document.getText());
if (!ast) return;

const pestTestDetails = await pestParser.getPestTestDetail(ast.children);
const pestTestItems = pestCommon.getPestTestItems(ast);

if (pestTestItems.length > 0) {
for (const t of pestTestItems) {
const startPostion = document.positionAt(t.startOffset);
const endPostion = document.positionAt(t.endOffset);

pestTestDetails.forEach((m) => {
if (m.startLine && m.endLine) {
const lens: CodeLens = {
range: Range.create(Position.create(m.startLine - 1, 0), Position.create(m.endLine, 0)),
range: Range.create(startPostion, endPostion),
command: {
title: codeLensTitle,
command: 'intelephense.pest.singleTest',
Expand All @@ -86,7 +92,7 @@ export class PestCodeLensProvider implements CodeLensProvider {

codeLenses.push(lens);
}
});
}
} catch (e) {
// noop
}
Expand Down
Loading

0 comments on commit 0475151

Please sign in to comment.