Skip to content

Commit

Permalink
Merge pull request #1168 from pjkaufman/new-integration-tests
Browse files Browse the repository at this point in the history
Add and Update Integration Tests
  • Loading branch information
pjkaufman authored Sep 13, 2024
2 parents 3a6fc5e + ef0c944 commit 07e1ea4
Show file tree
Hide file tree
Showing 25 changed files with 420 additions and 186 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'parserOptions': {
'ecmaVersion': 12,
'sourceType': 'module',
'project': ['./tsconfig.json', './packages/*/tsconfig.json'],
},
'plugins': [
'@typescript-eslint',
Expand All @@ -29,6 +30,7 @@ module.exports = {
'require-jsdoc': 'off',
'unicorn/template-indent': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
Expand Down
30 changes: 0 additions & 30 deletions __integration__/custom-commands.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import dedent from 'ts-dedent';
import TestLinterPlugin, {IntegrationTestCase} from './main.test';
import {Editor} from 'obsidian';
import expect from 'expect';

export const customCommandTestCases: IntegrationTestCase[] = [
{
Expand All @@ -17,33 +15,5 @@ export const customCommandTestCases: IntegrationTestCase[] = [
'name': 'Format all tables in this file',
}];
},
async assertions(editor: Editor) {
expect(editor.getValue()).toBe(dedent`
| one | two | three | four |
| ----- | --- | ----- | ------------- |
| 1 | ✓ | | |
| 2 | | ✓ | |
| 3 | | ✓ | ✓ |
| 4 | … | | someting else |
| Total | 100 | 20 | 300000000 |
${''}
| one | two | three | four |
| ----- | --- | ----- | ------------- |
| 1 | ✓ | | |
| 2 | | ✓ | |
| 3 | | ✓ | ✓ |
| 4 | … | | someting else |
| Total | 100 | 20 | 300000000 |
${''}
| one | two | three | four |
| ----- | --- | ----- | ------------- |
| 1 | ✓ | | |
| 2 | | ✓ | |
| 3 | | ✓ | ✓ |
| 4 | … | | someting else |
| Total | 100 | 20 | 300000000 |
${''}
`);
},
},
];
193 changes: 173 additions & 20 deletions __integration__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
import {Editor, MarkdownView, Plugin, TFile, normalizePath} from 'obsidian';
import {Editor, MarkdownView, Notice, Plugin, TFile, normalizePath} from 'obsidian';
import LinterPlugin from 'src/main';
import {obsidianModeTestCases} from './obsidian-mode.test';
import {setWorkspaceItemMode} from './utils.test';
import {customCommandTestCases} from './custom-commands.test';
import {obsidianYAMLRuleTestCases} from './yaml-rule.test';
import expect from 'expect';

export type IntegrationTestCase = {
name: string,
filePath: string,
setup?: (plugin: TestLinterPlugin, editor: Editor) => void,
assertions: (editor: Editor) => void,
setup?: (plugin: TestLinterPlugin, editor: Editor) => Promise<void>,
assertions?: (editor: Editor) => void,
modifyExpected?: (expectedText: string, file: TFile) => string,
}

type testStatus = {
name: string,
succeeded: boolean,
}

const testTimeout = 15000;

export default class TestLinterPlugin extends Plugin {
regularTests: Array<IntegrationTestCase> = [...obsidianModeTestCases];
regularTests: Array<IntegrationTestCase> = [...obsidianModeTestCases, ...obsidianYAMLRuleTestCases];
afterCacheUpdateTests: Array<IntegrationTestCase> = [...customCommandTestCases];
plugin: LinterPlugin;
private timeoutId: any = undefined;
private testRunNotice: Notice;

async onload() {
this.addCommand({
id: 'run-linter-tests',
name: 'Run Linter Tests',
callback: async () => {
if (this.timeoutId != undefined) {
clearTimeout(this.timeoutId);
}

await this.setup();
await this.runTests();

const testStatuses = [] as testStatus[];
const expectedTestCount = this.regularTests.length + this.afterCacheUpdateTests.length;
this.timeoutId = setTimeout(() => {
console.log(testStatuses);
if (testStatuses.length != expectedTestCount) {
if (this.testRunNotice) {
this.testRunNotice.setMessage(`❌: Tests took too long to run with only ${testStatuses.length} of ${expectedTestCount} tests running in ${testTimeout/1000}s.`);
} else {
console.log('❌', `Tests took too long to run with only ${testStatuses.length} of ${expectedTestCount} tests running in ${testTimeout/1000}s.`);
}
} else {
this.handleTestFinalization(testStatuses);
console.log(`✅ all ${expectedTestCount} tests have completed in the alloted time.`);
}
}, testTimeout);

await this.runTests(testStatuses, expectedTestCount);
},
});
}
Expand All @@ -37,7 +70,9 @@ export default class TestLinterPlugin extends Plugin {
}
}

async runTests() {
async runTests(testStatuses: testStatus[], totalTestCount: number) {
this.testRunNotice = new Notice('Starting the Linter\'s Integration Tests', 0);

const activeLeaf = this.getActiveLeaf();
if (!activeLeaf) {
console.error('failed to get active leaf');
Expand All @@ -48,6 +83,8 @@ export default class TestLinterPlugin extends Plugin {
const file = this.getFileFromPath(t.filePath);
if (!file) {
console.error('failed to get file: ' + t.filePath);

this.handleTestCompletion(t.name, false, testStatuses, totalTestCount);
continue;
}

Expand All @@ -61,23 +98,35 @@ export default class TestLinterPlugin extends Plugin {
}

await this.plugin.runLinterEditor(activeLeaf.editor);
await t.assertions(activeLeaf.editor);
await this.handleAssertions(t, activeLeaf, file);

console.log('✅', t.name);
this.handleTestCompletion(t.name, true, testStatuses, totalTestCount);
} catch (e) {
console.log('❌', t.name);
console.error(e);

this.handleTestCompletion(t.name, false, testStatuses, totalTestCount);
}

await this.resetFileContents(activeLeaf, originalText);
}

await this.runMetadataTests(this.afterCacheUpdateTests, activeLeaf);
if (testStatuses.length != this.regularTests.length) {
if (this.testRunNotice) {
this.testRunNotice.setMessage(`❌ failed to run all ${this.regularTests.length} regular tests before attempting to start the metadata tests.`);
} else {
console.log(`❌ failed to run all ${this.regularTests.length} regular tests before attempting to start the metadata tests.`);
}
return;
}

await this.runMetadataTests(this.afterCacheUpdateTests, activeLeaf, testStatuses, totalTestCount);
}

async runMetadataTests(tests: IntegrationTestCase[], activeLeaf: MarkdownView) {
async runMetadataTests(tests: IntegrationTestCase[], activeLeaf: MarkdownView, testStatuses: testStatus[], totalTestCount: number) {
let index = 0;
let originalText = await this.setupMetadataTest(this, tests[index], activeLeaf);
let originalText = await this.setupMetadataTest(this, tests[index], activeLeaf, testStatuses, totalTestCount);
if (originalText == null) {
return;
}
Expand All @@ -95,29 +144,29 @@ export default class TestLinterPlugin extends Plugin {

const t = tests[index];
try {
await t.assertions(activeLeaf.editor);
await this.handleAssertions(t, activeLeaf, file);

console.log('✅', t.name);
this.handleTestCompletion(t.name, true, testStatuses, totalTestCount);
} catch (e) {
console.log('❌', t.name);
console.error(e);

this.handleTestCompletion(t.name, false, testStatuses, totalTestCount);
}

await that.resetFileContents(activeLeaf, originalText);

originalText = null;
while (index+1 < tests.length && originalText == null) {
originalText = await that.setupMetadataTest(that, tests[++index], activeLeaf);
}

// remove the custom commands callback once all tests have run
if (index >= tests.length && originalText == null) {
if (index+1 < tests.length) {
originalText = await that.setupMetadataTest(that, tests[++index], activeLeaf, testStatuses, totalTestCount);
} else { // remove the custom commands callback once all tests have run
that.plugin.setCustomCommandCallback(null);
}
});
}

async setupMetadataTest(testPlugin: TestLinterPlugin, t: IntegrationTestCase, activeLeaf: MarkdownView): Promise<string> {
async setupMetadataTest(testPlugin: TestLinterPlugin, t: IntegrationTestCase, activeLeaf: MarkdownView, testStatuses: testStatus[], totalTestCount: number): Promise<string> {
const file = this.getFileFromPath(t.filePath);
if (!file) {
console.error('failed to get file: ' + t.filePath);
Expand All @@ -135,6 +184,8 @@ export default class TestLinterPlugin extends Plugin {

await testPlugin.plugin.runLinterEditor(activeLeaf.editor);
} catch (e) {
this.handleTestCompletion(t.name, false, testStatuses, totalTestCount);

console.log('❌', t.name);
console.error(e);
await testPlugin.resetFileContents(activeLeaf, originalText);
Expand All @@ -145,10 +196,26 @@ export default class TestLinterPlugin extends Plugin {
return originalText;
}

onunload(): void {
async onunload(): Promise<void> {
if (this.plugin) {
this.plugin.onunload();
await this.plugin.onunload();
}
}

private async handleAssertions(t: IntegrationTestCase, activeLeaf: MarkdownView, file: TFile) {
let expectedText = await this.getExpectedContents(t.filePath.replace('.md', '.linted.md'));
if (t.modifyExpected) {
expectedText = t.modifyExpected(expectedText, file);
}

expect(activeLeaf.editor.getValue()).toBe(expectedText);
if (t.assertions) {
t.assertions(activeLeaf.editor);
}

console.log('assertions complete for ' + t.filePath);

return;
}

private async resetFileContents(activeLeaf: MarkdownView, originalText: string) {
Expand All @@ -164,6 +231,16 @@ export default class TestLinterPlugin extends Plugin {
return activeLeaf;
}

private async getExpectedContents(filePath: string): Promise<string> {
const file = this.getFileFromPath(filePath);
if (!file) {
console.error('failed to get file: ' + filePath);
return;
}

return await this.app.vault.cachedRead(file);
}

private getFileFromPath(filePath: string): TFile {
const file = this.app.vault.getAbstractFileByPath(normalizePath(filePath));
if (file instanceof TFile) {
Expand All @@ -176,4 +253,80 @@ export default class TestLinterPlugin extends Plugin {
private async resetSettings() {
await this.plugin.loadSettings();
}

private handleTestCompletion(testName: string, succeeded: boolean, testStatuses: testStatus[], totalTestCount: number) {
testStatuses.push(
{
name: testName,
succeeded: succeeded,
});

let numberOfSuccesses = 0;
let numberOfFailures = 0;
for (const testResult of testStatuses) {
if (testResult.succeeded) {
numberOfSuccesses++;
} else {
numberOfFailures++;
}
}

if (this.testRunNotice) {
let message = `Running the Linter's Integration Tests (${testStatuses.length}/${totalTestCount})`;
message += '\nSo far there ';

if (numberOfFailures == 1) {
message += 'has been 1 failure';
} else {
message += `have been ${numberOfFailures} failures`;
}

message += ' and there ';

if (numberOfSuccesses == 1) {
message += 'has been 1 success';
} else {
message += `have been ${numberOfSuccesses} successes`;
}

message += '.';

this.testRunNotice.setMessage(message);
}
}

private handleTestFinalization(testStatuses: testStatus[]) {
if (this.testRunNotice) {
let message = `Finished running the Linter's Integration Tests.`;
message += '\nThere ';

let numberOfSuccesses = 0;
let numberOfFailures = 0;
for (const testResult of testStatuses) {
if (testResult.succeeded) {
numberOfSuccesses++;
} else {
numberOfFailures++;
}
}

if (numberOfFailures == 1) {
message += 'was 1 failure';
} else {
message += `have been ${numberOfFailures} failures`;
}

message += ' and there ';

if (numberOfSuccesses == 1) {
message += 'has been 1 success';
} else {
message += `have been ${numberOfSuccesses} successes`;
}

message += '. See the console for more details.';

this.testRunNotice.setMessage(message);
}
}
}
Loading

0 comments on commit 07e1ea4

Please sign in to comment.