Skip to content

Commit

Permalink
Merge pull request #966 from pjkaufman/custom-commands-cache-wait
Browse files Browse the repository at this point in the history
Custom Commands: Wait for Cache for Lint on File Change and Lint on Save
  • Loading branch information
pjkaufman authored Dec 10, 2023
2 parents 0f26e65 + eea48be commit a2f0b32
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ test-vault/.obsidian/*.json
!test-vault/.obsidian/plugins/hot-reload/*
test-vault/.obsidian/plugins/obsidian-linter/*
!test-vault/.obsidian/plugins/obsidian-linter/.hotreload
test-vault/.obsidian/plugins/table-editor-obsidian/*
!test-vault/.obsidian/plugins/table-editor-obsidian/.gitkeep

# obsidian
data.json
Expand Down
49 changes: 49 additions & 0 deletions __integration__/custom-commands.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import dedent from 'ts-dedent';
import TestLinterPlugin, {IntegrationTestCase} from './main.test';
import {Editor} from 'obsidian';
import expect from 'expect';

export const customCommandTestCases: IntegrationTestCase[] = [
{
name: 'Advanced Tables custom command running after the linting of a file should run just fine even though it relies on the cache',
filePath: 'custom-commands/table-format.md',
async setup(plugin: TestLinterPlugin, _: Editor) {
plugin.plugin.settings.ruleConfigs['consecutive-blank-lines'] = {
'enabled': true,
};

plugin.plugin.settings.lintCommands = [{
'id': 'table-editor-obsidian:format-all-tables',
'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 |
${''}
`);
},
},
];
143 changes: 129 additions & 14 deletions __integration__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {Editor, MarkdownView, Plugin, TFile, normalizePath} from 'obsidian';
import {Editor, EventRef, MarkdownView, 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';

export type IntegrationTestCase = {
name: string,
Expand All @@ -11,8 +12,10 @@ export type IntegrationTestCase = {
}

export default class TestLinterPlugin extends Plugin {
tests: Array<IntegrationTestCase> = [...obsidianModeTestCases];
regularTests: Array<IntegrationTestCase> = [...obsidianModeTestCases];
afterCacheUpdateTests: Array<IntegrationTestCase> = [...customCommandTestCases];
plugin: LinterPlugin;
private eventRefs: EventRef[] = [];

async onload() {
this.addCommand({
Expand All @@ -28,9 +31,11 @@ export default class TestLinterPlugin extends Plugin {
async setup() {
if (!this.plugin) {
this.plugin = new LinterPlugin(this.app, this.manifest);
}

await this.loadOrResetSettings();
await this.plugin.onload();
} else {
await this.resetSettings();
}
}

async runTests() {
Expand All @@ -40,37 +45,147 @@ export default class TestLinterPlugin extends Plugin {
return;
}

for (const t of this.tests) {
for (const t of this.regularTests) {
const file = this.getFileFromPath(t.filePath);
if (!file) {
console.error('failed to get file: ' + t.filePath);
continue;
}

await activeLeaf.leaf.openFile(file);

const originalText = activeLeaf.editor.getValue();

await this.loadOrResetSettings();
await this.resetSettings();

try {
if (t.setup) {
await t.setup(this, activeLeaf.editor);
}

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

console.log('✅', t.name);
} catch (e) {
console.log('❌', t.name);
console.error(e);
}

await this.resetFileContents(activeLeaf, originalText);
}

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

async runMetadataTests(tests: IntegrationTestCase[], activeLeaf: MarkdownView) {
let index = 0;
let originalText = await this.setupMetadataTest(this, tests[index], activeLeaf);
if (originalText == null) {
return;
}

const that = this;

this.plugin.setCustomCommandCallback(async (file: TFile) => {
if (file !== activeLeaf.file) {
return;
}

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

console.log('✅', t.name);
} catch (e) {
console.log('❌', t.name);
console.error(e);
}

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) {
that.plugin.setCustomCommandCallback(null);
}
});
}

async setupMetadataTest(testPlugin: TestLinterPlugin, t: IntegrationTestCase, activeLeaf: MarkdownView): Promise<string> {
const file = this.getFileFromPath(t.filePath);
if (!file) {
console.error('failed to get file: ' + t.filePath);
return null;
}

await activeLeaf.leaf.openFile(file);
const originalText = activeLeaf.editor.getValue();
await testPlugin.resetSettings();

try {
if (t.setup) {
await t.setup(this, activeLeaf.editor);
}

testPlugin.plugin.runLinterEditor(activeLeaf.editor);
} catch (e) {
console.log('❌', t.name);
console.error(e);
await testPlugin.resetFileContents(activeLeaf, originalText);

return null;
}

return originalText;
}

addMetadataCacheTestCallback(t: IntegrationTestCase, activeLeaf: MarkdownView, originalText: string) {
// we use this to make sure a second cache update is not able to run before the first one
// for the file we are looking for has completed
let alreadyRun = false;
const eventRef = this.app.metadataCache.on('changed', async (updatedFile: TFile) => {
if (activeLeaf.file !== updatedFile || alreadyRun ) {
return;
}

alreadyRun = true;

try {
await t.assertions(activeLeaf.editor);

t.assertions(activeLeaf.editor);
console.log('✅', t.name);
} catch (e) {
console.log('❌', t.name);
console.error(e);
} finally {
if (activeLeaf) {
activeLeaf.editor.setValue(originalText);
await setWorkspaceItemMode(this.app, true);
}
this.app.workspace.offref(eventRef);
this.eventRefs.remove(eventRef);

await this.resetFileContents(activeLeaf, originalText);
}
});

this.registerEvent(eventRef);
this.eventRefs.push(eventRef);
}

onunload(): void {
for (const eventRef of this.eventRefs) {
this.app.workspace.offref(eventRef);
}

if (this.plugin) {
this.plugin.onunload();
}
}

private async resetFileContents(activeLeaf: MarkdownView, originalText: string) {
if (activeLeaf) {
activeLeaf.editor.setValue(originalText);
await setWorkspaceItemMode(this.app, true);
}
}

Expand All @@ -89,7 +204,7 @@ export default class TestLinterPlugin extends Plugin {
return null;
}

private async loadOrResetSettings() {
private async resetSettings() {
await this.plugin.loadSettings();
}
}
1 change: 1 addition & 0 deletions src/lang/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default {
'unknown-error': 'An unknown error occurred during linting.',
'moment-locale-not-found': 'Trying to switch Moment.js locale to {MOMENT_LOCALE}, got {CURRENT_LOCALE}',
'file-change-lint-message-start': 'Linted',
'custom-command-callback-warning': 'Please only set the custom command callback for integration tests.',

// rules-runner.ts
'pre-rules': 'rules before regular rules',
Expand Down
Loading

0 comments on commit a2f0b32

Please sign in to comment.