Skip to content

Commit

Permalink
E2e back (#75)
Browse files Browse the repository at this point in the history
* working with static paths

* Works without node modules

* fixes paths

* Changes workflows

* Possible fix

* Adds comment

* Adds latest to the build

* Removes vscode settings
  • Loading branch information
KostkaBrukowa committed May 2, 2024
1 parent c496c08 commit bae7aa6
Show file tree
Hide file tree
Showing 25 changed files with 572 additions and 159 deletions.
20 changes: 11 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,29 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 15.x
uses: actions/setup-node@v2
- uses: actions/checkout@v4
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: '15.x'
node-version: '20.x'
- run: npm i
- run: npm run lint
- run: npm run prettier:ci

build:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
typescript_version: [4.x, 5.2.2, latest]

steps:
- uses: actions/checkout@v2
- name: Use Node.js 15.x
uses: actions/setup-node@v2
- uses: actions/checkout@v4
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: '15.x'
node-version: '20.x'
- run: npm i
- run: npm i -D typescript@${{ matrix.typescript_version }}
- run: npm run build
- run: npm run test
6 changes: 3 additions & 3 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: '14.x'
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- run: npm install
- run: npm run build
Expand Down
4 changes: 4 additions & 0 deletions e2e/fixtures/default-config/ignored.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-strict-ignore
const text: string = null;

export {};
3 changes: 3 additions & 0 deletions e2e/fixtures/default-config/strict.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
18 changes: 18 additions & 0 deletions e2e/fixtures/default-config/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018"],
"baseUrl": "./",
"outDir": "./dist",
"strict": false,
"pretty": true,
"esModuleInterop": true,
"noImplicitAny": true,
"plugins": [
{
"name": "typescript-strict-plugin"
}
]
}
}
4 changes: 4 additions & 0 deletions e2e/fixtures/non-root-config/nested/ignored.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-strict-ignore
const text: string = null;

export {};
3 changes: 3 additions & 0 deletions e2e/fixtures/non-root-config/nested/strict.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
18 changes: 18 additions & 0 deletions e2e/fixtures/non-root-config/nested/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018"],
"baseUrl": "./",
"outDir": "./dist",
"strict": false,
"pretty": true,
"esModuleInterop": true,
"noImplicitAny": true,
"plugins": [
{
"name": "typescript-strict-plugin"
}
]
}
}
3 changes: 3 additions & 0 deletions e2e/fixtures/path-config/excluded/excluded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
3 changes: 3 additions & 0 deletions e2e/fixtures/path-config/excluded/excluded2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-strict
const text: string = null;

export {};
3 changes: 3 additions & 0 deletions e2e/fixtures/path-config/included/included.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
3 changes: 3 additions & 0 deletions e2e/fixtures/path-config/included/included2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const text: string = null;

export {};
18 changes: 18 additions & 0 deletions e2e/fixtures/path-config/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018"],
"baseUrl": "./",
"outDir": "./dist",
"strict": false,
"esModuleInterop": true,
"noImplicitAny": true,
"plugins": [
{
"name": "typescript-strict-plugin",
"paths": ["./included"]
}
]
}
}
29 changes: 29 additions & 0 deletions e2e/fixtures/paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import path from 'path';

export const fixtureWithDefaultConfig = {
projectPath: path.resolve(__dirname, 'default-config'),
filePaths: {
strict: 'strict.ts',
ignored: 'ignored.ts',
},
};

export const fixtureWithPathConfig = {
projectPath: path.resolve(__dirname, 'path-config'),
filePaths: {
included: 'included/included.ts',
included2: 'included/included2.ts',
excluded: 'excluded/excluded.ts',
excluded2: 'excluded/excluded2.ts',
excludedWithStrictComment: 'excluded/excludedWithStrictComment.ts',
},
};

export const fixtureWithNonRootConfig = {
projectPath: path.resolve(__dirname, 'non-root-config'),
filePaths: {
strict: 'strict.ts',
ignored: 'ignored.ts',
},
args: ['--project', './nested/tsconfig.json'],
};
17 changes: 17 additions & 0 deletions e2e/plugin/multipleFile.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { getMultipleDiagnostics } from './utils/getMultipleDiagnostics';
import { fixtureWithDefaultConfig } from '../fixtures/paths';

describe('multiple file diagnostics', () => {
it('should show errors only on file with strict comment', async () => {
// given
const { projectPath, filePaths } = fixtureWithDefaultConfig;
const fileList = [filePaths.strict, filePaths.ignored];

// when
const diagnostics = await getMultipleDiagnostics(projectPath, fileList);

// then
expect(diagnostics[0]).toHaveLength(1);
expect(diagnostics[1]).toHaveLength(0);
});
});
65 changes: 65 additions & 0 deletions e2e/plugin/singleFile.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { getDiagnostics } from './utils/getDiagnostics';
import {
fixtureWithDefaultConfig,
fixtureWithNonRootConfig,
fixtureWithPathConfig,
} from '../fixtures/paths';

describe('single file diagnostics', () => {
it('should enable strict mode by default in project without config', async () => {
// given
const { projectPath, filePaths } = fixtureWithDefaultConfig;

// when
const diagnostics = await getDiagnostics(projectPath, filePaths.strict);

// then
expect(diagnostics).toHaveLength(1);
});

it('should not enable strict mode in ignored file', async () => {
// given
const { projectPath, filePaths } = fixtureWithDefaultConfig;

// when
const diagnostics = await getDiagnostics(projectPath, filePaths.ignored);

// then
expect(diagnostics).toHaveLength(0);
});

it('should not enable strict mode when file is not on path', async () => {
// given
const { projectPath, filePaths } = fixtureWithPathConfig;

// when
const diagnostics = await getDiagnostics(projectPath, filePaths.excluded);

// then
expect(diagnostics).toHaveLength(0);
});

it('should enable strict mode when file is not on path and contains strict comment', async () => {
// given
const { projectPath, filePaths } = fixtureWithPathConfig;

// when
const diagnostics = await getDiagnostics(projectPath, filePaths.excludedWithStrictComment);

// then
expect(diagnostics).toHaveLength(1);
});

it('should enable strict mode with a relative path config', async () => {
// given
const { projectPath, filePaths } = fixtureWithPathConfig;

// when
const diagnosticsIncluded = await getDiagnostics(projectPath, filePaths.included);
const diagnosticsExcluded = await getDiagnostics(projectPath, filePaths.excluded);

// then
expect(diagnosticsIncluded).toHaveLength(1);
expect(diagnosticsExcluded).toHaveLength(0);
});
});
82 changes: 82 additions & 0 deletions e2e/plugin/utils/TSServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// /* implementation taken from https://github.com/Quramy/ts-graphql-plugin/blob/master/e2e/fixtures/lang-server.js */
import { ChildProcess, fork } from 'child_process';
import { EventEmitter } from 'events';
import path from 'path';

export interface ServerResponse {
command: string;
event: string;
type: string;
body: any;
}

export interface ServerRequest {
command?: string;
event?: string;
type?: string;
arguments: any;
}

export class TSServer {
public responses: ServerResponse[];

private _responseEventEmitter: NodeJS.EventEmitter;
private _responseCommandEmitter: NodeJS.EventEmitter;
private _exitPromise: Promise<string>;
private _isClosed: boolean;
private _server: ChildProcess;
private _seq: number;

constructor(projectPath: string) {
this._responseEventEmitter = new EventEmitter();
this._responseCommandEmitter = new EventEmitter();
const tsserverPath = require.resolve('typescript/lib/tsserver');

const server = fork(tsserverPath, {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
cwd: projectPath,
// env: { TSS_LOG: '-logToFile true -file ./ts.log -level verbose' }, // creates tsserver log from tests
});
this._exitPromise = new Promise((resolve, reject) => {
server.on('exit', (code: string) => resolve(code));
server.on('error', (reason: string) => reject(reason));
});
server.stdout?.setEncoding('utf-8');
server.stdout?.on('data', (data: string) => {
const [, , res] = data.split('\n');
const obj = JSON.parse(res) as ServerResponse;
if (obj.type === 'event') {
this._responseEventEmitter.emit(obj.event, obj);
} else if (obj.type === 'response') {
this._responseCommandEmitter.emit(obj.command, obj);
}
this.responses.push(obj);
});
this._isClosed = false;
this._server = server;
this._seq = 0;
this.responses = [];
}

send(command: ServerRequest) {
const seq = ++this._seq;
const req = JSON.stringify(Object.assign({ seq: seq, type: 'request' }, command)) + '\n';
this._server.stdin?.write(req);
}

close() {
if (!this._isClosed) {
this._isClosed = true;
this._server.stdin?.end();
}
return this._exitPromise;
}

waitEvent(eventName: string) {
return new Promise((res) => this._responseEventEmitter.once(eventName, () => res(undefined)));
}

waitResponse(eventName: string) {
return new Promise((res) => this._responseCommandEmitter.once(eventName, () => res(undefined)));
}
}
35 changes: 35 additions & 0 deletions e2e/plugin/utils/getDiagnostics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @ts-ignore
import { TSServer, ServerResponse } from './TSServer';
import path, { resolve } from 'path';
import { readFileSync } from 'fs';

function findResponse(responses: ServerResponse[], eventName: string) {
return responses.find((response) => response.event === eventName);
}

export async function getDiagnostics(projectPath: string, filePath: string) {
const server = new TSServer(projectPath);

const file = resolve(projectPath, filePath);

const fileContent = readFileSync(file, 'utf-8');

server.send({
command: 'updateOpen',
arguments: {
openFiles: [{ file: file, fileContent, scriptKindName: 'TS', projectRootPath: projectPath }],
},
});

await server.waitEvent('projectLoadingFinish');

server.send({ command: 'geterr', arguments: { files: [file], delay: 10 } });

await server.waitEvent('semanticDiag');

await server.close();

const semanticDiagEvent = findResponse(server.responses, 'semanticDiag');

return semanticDiagEvent?.body.diagnostics;
}

0 comments on commit bae7aa6

Please sign in to comment.