Skip to content

test(plugin-typescript-e2e): add basic e2e tests #974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions e2e/plugin-typescript-e2e/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import tseslint from 'typescript-eslint';
import baseConfig from '../../eslint.config.js';

export default tseslint.config(...baseConfig, {
files: ['**/*.ts'],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { CoreConfig } from '@code-pushup/models';
import {
getCategoryRefsFromGroups,
typescriptPlugin,
} from '@code-pushup/typescript-plugin';

export default {
plugins: [await typescriptPlugin()],
categories: [
{
slug: 'typescript-quality',
title: 'Typescript',
refs: getCategoryRefsFromGroups(),
},
],
} satisfies CoreConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function test() {
return 'test';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = { ; // Error: TS1136: Property assignment expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 2683 - NoImplicitThis: 'this' implicitly has type 'any'.
function noImplicitThisTS2683() {
console.log(this.value); // Error 2683
}

// 2531 - StrictNullChecks: Object is possibly 'null'.
const strictNullChecksTS2531: string = null;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Standalone {
override method() { // Error: TS4114 - 'override' modifier can only be used in a class derived from a base class.
console.log("Standalone method");
}
}
const s = Standalone;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { test } from '../exclude/utils';

// TS6059:: File 'exclude/utils.ts' is not under 'rootDir' '.../configuration-errors'. 'rootDir' is expected to contain all source files.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"rootDir": "./src",
"target": "ES6",
"module": "CommonJS",
"strict": true,
"verbatimModuleSyntax": false
},
"include": ["src/**/*.ts"]
}
23 changes: 23 additions & 0 deletions e2e/plugin-typescript-e2e/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "plugin-typescript-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/plugin-typescript-e2e/src",
"projectType": "application",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["e2e/plugin-typescript-e2e/**/*.ts"]
}
},
"e2e": {
"executor": "@nx/vite:test",
"options": {
"configFile": "e2e/plugin-typescript-e2e/vite.config.e2e.ts"
}
}
},
"implicitDependencies": ["cli", "plugin-typescript"],
"tags": ["scope:plugin", "type:e2e"]
}
218 changes: 218 additions & 0 deletions e2e/plugin-typescript-e2e/tests/__snapshots__/collect.e2e.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`PLUGIN collect report with typescript-plugin NPM package > should run plugin over CLI and creates report.json 1`] = `
{
"categories": [
{
"refs": [
{
"plugin": "typescript",
"slug": "problems",
"type": "group",
"weight": 1,
},
{
"plugin": "typescript",
"slug": "ts-configuration",
"type": "group",
"weight": 1,
},
{
"plugin": "typescript",
"slug": "miscellaneous",
"type": "group",
"weight": 1,
},
],
"slug": "typescript-quality",
"title": "Typescript",
},
],
"packageName": "@code-pushup/core",
"plugins": [
{
"audits": [
{
"description": "Errors that occur during parsing and lexing of TypeScript source code",
"details": {
"issues": [
{
"message": "TS1136: Property assignment expected.",
"severity": "error",
"source": {
"file": "tmp/e2e/plugin-typescript-e2e/src/1-syntax-errors.ts",
"position": {
"startLine": 1,
},
},
},
],
},
"displayValue": "1 issue",
"score": 0,
"slug": "syntax-errors",
"title": "Syntax errors",
"value": 1,
},
{
"description": "Errors that occur during type checking and type inference",
"details": {
"issues": [
{
"message": "TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.",
"severity": "error",
"source": {
"file": "tmp/e2e/plugin-typescript-e2e/src/2-semantic-errors.ts",
"position": {
"startLine": 3,
},
},
},
{
"message": "TS2322: Type 'null' is not assignable to type 'string'.",
"severity": "error",
"source": {
"file": "tmp/e2e/plugin-typescript-e2e/src/2-semantic-errors.ts",
"position": {
"startLine": 7,
},
},
},
],
},
"displayValue": "2 issues",
"score": 0,
"slug": "semantic-errors",
"title": "Semantic errors",
"value": 2,
},
{
"description": "Errors that occur during TypeScript language service operations",
"details": {
"issues": [
{
"message": "TS4112: This member cannot have an 'override' modifier because its containing class 'Standalone' does not extend another class.",
"severity": "error",
"source": {
"file": "tmp/e2e/plugin-typescript-e2e/src/4-languale-service.ts",
"position": {
"startLine": 2,
},
},
},
],
},
"displayValue": "1 issue",
"score": 0,
"slug": "declaration-and-language-service-errors",
"title": "Declaration and language service errors",
"value": 1,
},
{
"description": "Errors that occur during TypeScript internal operations",
"displayValue": "0 issues",
"score": 1,
"slug": "internal-errors",
"title": "Internal errors",
"value": 0,
},
{
"description": "Errors that occur when parsing TypeScript configuration files",
"details": {
"issues": [
{
"message": "TS6059: File './exclude/utils.ts' is not under 'rootDir' 'src'. 'rootDir' is expected to contain all source files.",
"severity": "error",
"source": {
"file": "tmp/e2e/plugin-typescript-e2e/src/6-configuration-errors.ts",
"position": {
"startLine": 1,
},
},
},
],
},
"displayValue": "1 issue",
"score": 0,
"slug": "configuration-errors",
"title": "Configuration errors",
"value": 1,
},
{
"description": "Errors related to no implicit any compiler option",
"displayValue": "0 issues",
"score": 1,
"slug": "no-implicit-any-errors",
"title": "No implicit any errors",
"value": 0,
},
{
"description": "Errors that do not match any known TypeScript error code",
"displayValue": "0 issues",
"score": 1,
"slug": "unknown-codes",
"title": "Unknown codes",
"value": 0,
},
],
"description": "Official Code PushUp Typescript plugin.",
"docsUrl": "https://www.npmjs.com/package/@code-pushup/typescript-plugin/",
"groups": [
{
"description": "Syntax, semantic, and internal compiler errors are critical for identifying and preventing bugs.",
"refs": [
{
"slug": "syntax-errors",
"weight": 1,
},
{
"slug": "semantic-errors",
"weight": 1,
},
{
"slug": "no-implicit-any-errors",
"weight": 1,
},
],
"slug": "problems",
"title": "Problems",
},
{
"description": "TypeScript configuration and options errors ensure correct project setup, reducing risks from misconfiguration.",
"refs": [
{
"slug": "configuration-errors",
"weight": 1,
},
],
"slug": "ts-configuration",
"title": "Configuration",
},
{
"description": "Errors that do not bring any specific value to the developer, but are still useful to know.",
"refs": [
{
"slug": "unknown-codes",
"weight": 1,
},
{
"slug": "internal-errors",
"weight": 1,
},
{
"slug": "declaration-and-language-service-errors",
"weight": 1,
},
],
"slug": "miscellaneous",
"title": "Miscellaneous",
},
],
"icon": "typescript",
"packageName": "@code-pushup/typescript-plugin",
"slug": "typescript",
"title": "Typescript",
},
],
}
`;
73 changes: 73 additions & 0 deletions e2e/plugin-typescript-e2e/tests/collect.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { cp } from 'node:fs/promises';
import path from 'node:path';
import { afterAll, beforeAll, expect } from 'vitest';
import { type Report, reportSchema } from '@code-pushup/models';
import { nxTargetProject } from '@code-pushup/test-nx-utils';
import {
E2E_ENVIRONMENTS_DIR,
TEST_OUTPUT_DIR,
omitVariableReportData,
removeColorCodes,
teardownTestFolder,
} from '@code-pushup/test-utils';
import { executeProcess, readJsonFile } from '@code-pushup/utils';

describe('PLUGIN collect report with typescript-plugin NPM package', () => {
const envRoot = path.join(E2E_ENVIRONMENTS_DIR, nxTargetProject());
const distRoot = path.join(envRoot, TEST_OUTPUT_DIR);

const fixturesDir = path.join(
'e2e',
nxTargetProject(),
'mocks',
'fixtures',
'default-setup',
);

beforeAll(async () => {
await cp(fixturesDir, envRoot, { recursive: true });
});

afterAll(async () => {
await teardownTestFolder(distRoot);
});

it('should run plugin over CLI and creates report.json', async () => {
const outputDir = path.join(
path.relative(envRoot, distRoot),
'create-report',
'.code-pushup',
);

const { code, stdout } = await executeProcess({
command: 'npx',
// verbose exposes audits with perfect scores that are hidden in the default stdout
args: [
'@code-pushup/cli',
'collect',
'--no-progress',
'--verbose',
`--persist.outputDir=${outputDir}`,
],
cwd: envRoot,
});

expect(code).toBe(0);
const cleanStdout = removeColorCodes(stdout);

expect(cleanStdout).toMatch(/● Semantic errors\s+\d+ issue/);
expect(cleanStdout).toMatch(/● Configuration errors\s+\d+ issue/);
expect(cleanStdout).toMatch(
/● Declaration and language service errors\s+\d+ issue/,
);
expect(cleanStdout).toMatch(/● Syntax errors\s+\d+ issue/);
expect(cleanStdout).toMatch(/● Internal errors\s+\d+ issue/);
expect(cleanStdout).toMatch(/● No implicit any errors\s+\d+ issue/);

const reportJson = await readJsonFile<Report>(
path.join(envRoot, outputDir, 'report.json'),
);
expect(() => reportSchema.parse(reportJson)).not.toThrow();
expect(omitVariableReportData(reportJson)).toMatchSnapshot();
});
});
Loading