-
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate
test/e2e/__tests__/code-actions.ts
- Loading branch information
1 parent
1e0e603
commit 96e4ad5
Showing
2 changed files
with
120 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,196 @@ | ||
/* eslint-disable jest/no-standalone-expect */ | ||
import fs from 'fs/promises'; | ||
import path from 'path'; | ||
import process from 'process'; | ||
import * as assert from 'node:assert/strict'; | ||
import { EOL } from 'node:os'; | ||
|
||
import { workspace, Selection, Position } from 'vscode'; | ||
|
||
import pWaitFor from 'p-wait-for'; | ||
import { | ||
commands, | ||
extensions, | ||
workspace, | ||
Selection, | ||
Range, | ||
Position, | ||
CodeAction, | ||
TextEditor, | ||
} from 'vscode'; | ||
import { ApiEvent, PublicApi } from '../../../src/extension/index'; | ||
|
||
const getCodeActions = async (editor: TextEditor): Promise<CodeAction[]> => | ||
(await commands.executeCommand( | ||
'vscode.executeCodeActionProvider', | ||
editor.document.uri, | ||
new Range(editor.selection.start, editor.selection.end), | ||
)) ?? []; | ||
|
||
const serializeCodeActions = (actions: CodeAction[]) => | ||
actions.map((action) => ({ | ||
...action, | ||
...(action.edit ? { edit: action.edit?.entries()?.map(([, edits]) => ['<uri>', edits]) } : {}), | ||
})); | ||
|
||
const cssPath = path.resolve(workspaceDir, 'code-actions/test.css'); | ||
const jsPath = path.resolve(workspaceDir, 'code-actions/test.js'); | ||
const settingsPath = path.resolve(workspaceDir, 'code-actions/.vscode/settings.json'); | ||
|
||
// TODO: Investigate why editing tests intermittently fail on CI | ||
const localIt = process.env.CI ? it.skip : it; | ||
assertCommand, | ||
assertTextEdits, | ||
waitForDiagnostics, | ||
openDocument, | ||
closeAllEditors, | ||
restoreFile, | ||
setupSettings, | ||
getCodeActions, | ||
} from '../helpers'; | ||
|
||
const cssPath = 'code-actions/test.css'; | ||
const jsPath = 'code-actions/test.js'; | ||
|
||
describe('Code actions', () => { | ||
beforeAll(async () => { | ||
const api = (await extensions.getExtension('stylelint.vscode-stylelint')?.exports) as PublicApi; | ||
|
||
await pWaitFor(() => api.codeActionReady, { timeout: 5000 }); | ||
}); | ||
|
||
let savedFiles: Map<string, string>; | ||
|
||
beforeEach(async () => { | ||
savedFiles = new Map([ | ||
[cssPath, await fs.readFile(cssPath, 'utf8')], | ||
[settingsPath, await fs.readFile(settingsPath, 'utf8')], | ||
]); | ||
}); | ||
|
||
afterEach(async () => { | ||
for (const [filePath, content] of savedFiles.entries()) { | ||
await fs.writeFile(filePath, content); | ||
} | ||
await closeAllEditors(); | ||
}); | ||
|
||
restoreFile(cssPath); | ||
restoreFile(jsPath); | ||
|
||
it('should provide code actions for problems', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
await waitForDiagnostics(editor); | ||
|
||
editor.selection = new Selection(new Position(1, 2), new Position(1, 2)); | ||
|
||
const actions = await getCodeActions(editor); | ||
const actions = await getCodeActions(editor, new Selection(1, 2, 1, 2)); | ||
|
||
expect(serializeCodeActions(actions)).toMatchSnapshot(); | ||
assert.equal(actions.length, 3); | ||
assertTextEdits(actions[0].edit?.get(editor.document.uri), [ | ||
{ | ||
newText: ` /* stylelint-disable-next-line indentation */${EOL}`, | ||
range: [1, 0, 1, 0], | ||
}, | ||
]); | ||
assertTextEdits(actions[1].edit?.get(editor.document.uri), [ | ||
{ | ||
newText: `/* stylelint-disable indentation */${EOL}`, | ||
range: [0, 0, 0, 0], | ||
}, | ||
]); | ||
assertCommand(actions[2].command, { | ||
title: 'Open documentation for indentation', | ||
command: 'stylelint.openRuleDoc', | ||
arguments: [{ uri: 'https://stylelint.io/user-guide/rules/indentation' }], | ||
}); | ||
}); | ||
|
||
it('should not provide disable code actions for disable reports', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
await waitForDiagnostics(editor); | ||
|
||
editor.selection = new Selection(new Position(2, 4), new Position(2, 4)); | ||
const actions = await getCodeActions(editor, new Selection(2, 4, 2, 4)); | ||
|
||
const actions = await getCodeActions(editor); | ||
|
||
expect(actions).toHaveLength(0); | ||
assert.equal(actions.length, 0); | ||
}); | ||
|
||
test('should run auto-fix action on save', async () => { | ||
it('should run auto-fix action on save', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
await waitForDiagnostics(editor); | ||
|
||
await getCodeActions(editor); | ||
|
||
// API won't save unless we dirty the document, unlike saving via the UI | ||
await editor.edit((editBuilder) => { | ||
editBuilder.insert(new Position(3, 0), ' '); | ||
}); | ||
|
||
await editor.document.save(); | ||
|
||
expect(editor.document.getText()).toMatchSnapshot(); | ||
assert.equal( | ||
editor.document.getText(), | ||
`a { | ||
font-size: 1.2em; | ||
/* stylelint-disable-next-line comment-no-empty */ | ||
color: #00; | ||
} | ||
`, | ||
); | ||
}); | ||
|
||
localIt('should disable rules for an entire file', async () => { | ||
it('should disable rules for an entire file', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
await waitForDiagnostics(editor); | ||
|
||
editor.selection = new Selection(new Position(1, 2), new Position(1, 2)); | ||
|
||
const actions = await getCodeActions(editor); | ||
const actions = await getCodeActions(editor, new Selection(1, 2, 1, 2)); | ||
|
||
const fileAction = actions.find((action) => | ||
action.title.match(/^Disable .+ for the entire file$/), | ||
); | ||
|
||
expect(fileAction?.edit).toBeDefined(); | ||
assert.ok(fileAction?.edit); | ||
|
||
await workspace.applyEdit(fileAction!.edit!); | ||
await workspace.applyEdit(fileAction.edit); | ||
|
||
expect(editor.document.getText()).toMatchSnapshot(); | ||
assert.equal( | ||
editor.document.getText(), | ||
`/* stylelint-disable indentation */ | ||
a { | ||
font-size: 1.2em; | ||
/* stylelint-disable-next-line comment-no-empty */ | ||
color: #00; | ||
} | ||
`, | ||
); | ||
}); | ||
|
||
localIt('should disable rules for an entire file with a shebang', async () => { | ||
it('should disable rules for an entire file with a shebang', async () => { | ||
const editor = await openDocument(jsPath); | ||
|
||
await waitForDiagnostics(editor); | ||
|
||
editor.selection = new Selection(new Position(6, 9), new Position(6, 9)); | ||
|
||
const actions = await getCodeActions(editor); | ||
const actions = await getCodeActions(editor, new Selection(6, 9, 6, 9)); | ||
|
||
const fileAction = actions.find((action) => | ||
action.title.match(/^Disable .+ for the entire file$/), | ||
); | ||
|
||
expect(fileAction?.edit).toBeDefined(); | ||
|
||
await workspace.applyEdit(fileAction!.edit!); | ||
assert.ok(fileAction?.edit); | ||
|
||
expect(editor.document.getText()).toMatchSnapshot(); | ||
}); | ||
|
||
localIt( | ||
'should disable rules for a specific line with a comment on the previous line', | ||
async () => { | ||
const editor = await openDocument(cssPath); | ||
await workspace.applyEdit(fileAction.edit); | ||
|
||
editor.selection = new Selection(new Position(1, 2), new Position(1, 2)); | ||
assert.equal( | ||
editor.document.getText(), | ||
`#!/usr/bin/env node | ||
/* stylelint-disable color-no-invalid-hex */ | ||
/* eslint-disable node/shebang */ | ||
'use strict'; | ||
await waitForDiagnostics(editor); | ||
const css = css\` | ||
.foo { | ||
color: #00; | ||
} | ||
\`; | ||
`, | ||
); | ||
}); | ||
|
||
const actions = await getCodeActions(editor); | ||
const lineAction = actions.find((action) => action.title.match(/^Disable .+ for this line$/)); | ||
it('should disable rules for a specific line with a comment on the previous line', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
expect(lineAction?.edit).toBeDefined(); | ||
await waitForDiagnostics(editor); | ||
|
||
await workspace.applyEdit(lineAction!.edit!); | ||
const actions = await getCodeActions(editor, new Selection(1, 2, 1, 2)); | ||
const lineAction = actions.find((action) => action.title.match(/^Disable .+ for this line$/)); | ||
|
||
expect(editor.document.getText()).toMatchSnapshot(); | ||
}, | ||
); | ||
assert.ok(lineAction?.edit); | ||
|
||
localIt('should disable rules for a specific line with a comment on the same line', async () => { | ||
const settingsEditor = await openDocument(settingsPath); | ||
await workspace.applyEdit(lineAction.edit); | ||
|
||
await settingsEditor.edit((edit) => | ||
edit.insert( | ||
new Position(10, 2), | ||
',\n\t"stylelint.codeAction.disableRuleComment": { "location": "sameLine" }', | ||
), | ||
assert.equal( | ||
editor.document.getText(), | ||
`a { | ||
/* stylelint-disable-next-line indentation */ | ||
font-size: 1.2em; | ||
/* stylelint-disable-next-line comment-no-empty */ | ||
color: #00; | ||
} | ||
`, | ||
); | ||
}); | ||
|
||
const resetPromise = waitForApiEvent(ApiEvent.DidResetConfiguration); | ||
|
||
await settingsEditor.document.save(); | ||
await resetPromise; | ||
//await openDocument(cssPath); | ||
//await commands.executeCommand('workbench.action.closeActiveEditor'); | ||
context('when "stylelint.codeAction.disableRuleComment" is set to "sameLine"', () => { | ||
setupSettings({ 'stylelint.codeAction.disableRuleComment': { location: 'sameLine' } }); | ||
|
||
const editor = await openDocument(cssPath); | ||
|
||
await waitForDiagnostics(editor); | ||
it('should disable rules for a specific line with a comment on the same line', async () => { | ||
const editor = await openDocument(cssPath); | ||
|
||
editor.selection = new Selection(new Position(1, 2), new Position(1, 2)); | ||
await waitForDiagnostics(editor); | ||
|
||
const actions = await getCodeActions(editor); | ||
const lineAction = actions.find((action) => action.title.match(/^Disable .+ for this line$/)); | ||
const actions = await getCodeActions(editor, new Selection(1, 2, 1, 2)); | ||
const lineAction = actions.find((action) => action.title.match(/^Disable .+ for this line$/)); | ||
|
||
expect(lineAction?.edit).toBeDefined(); | ||
assert.ok(lineAction?.edit); | ||
|
||
await workspace.applyEdit(lineAction!.edit!); | ||
await workspace.applyEdit(lineAction.edit); | ||
|
||
expect(editor.document.getText()).toMatchSnapshot(); | ||
assert.equal( | ||
editor.document.getText(), | ||
`a { | ||
font-size: 1.2em; /* stylelint-disable-line indentation */ | ||
/* stylelint-disable-next-line comment-no-empty */ | ||
color: #00; | ||
} | ||
`, | ||
); | ||
}); | ||
}); | ||
}); |