blob: 24e53adb5550cfdcfb3df78f424b8c7808fe76b8 [file] [log] [blame]
import 'jasmine';
import * as ts from 'typescript';
import {Failure, Fix} from '../../failure';
import {ConformancePatternRule, PatternKind} from '../../rules/conformance_pattern_rule';
import {buildReplacementFixer, Fixer, maybeAddNamedImport, maybeAddNamespaceImport} from '../../util/fixer';
import {compile, compileAndCheck, customMatchers} from '../../util/testing/test_support';
const uppercaseFixer: Fixer = {
getFixForFlaggedNode(node: ts.Node): Fix {
return {
changes: [{
start: node.getStart(),
end: node.getEnd(),
replacement: node.getText().toUpperCase(),
sourceFile: node.getSourceFile(),
}]
};
}
};
const uppercaseFixerBuilt: Fixer = buildReplacementFixer((node: ts.Node) => {
return {replaceWith: node.getText().toUpperCase()};
})
// The initial config and source off which we run those checks.
const baseConfig = {
errorMessage: 'found citation',
kind: PatternKind.BANNED_PROPERTY_WRITE,
values: ['HTMLQuoteElement.prototype.cite'],
};
const source = `export {};\n` +
`const q = document.createElement('q');\n` +
`q.cite = 'some example string';\n`;
describe('ConformancePatternRule\'s fixer', () => {
describe('Generates basic fixes', () => {
it('for a single match', () => {
const rule = new ConformancePatternRule(baseConfig, uppercaseFixer);
const results = compileAndCheck(rule, source);
expect(results).toHaveNFailures(1, baseConfig);
expect(results[0]).toBeFailureMatching({
matchedCode: `q.cite = 'some example string'`,
messageText: 'found citation'
});
expect(results[0]).toHaveFixMatching([
{start: 50, end: 80, replacement: `Q.CITE = 'SOME EXAMPLE STRING'`}
]);
});
it('for a single match (alternate fixer)', () => {
const rule = new ConformancePatternRule(baseConfig, uppercaseFixerBuilt);
const results = compileAndCheck(rule, source);
expect(results).toHaveNFailures(1, baseConfig);
expect(results[0]).toBeFailureMatching({
matchedCode: `q.cite = 'some example string'`,
messageText: 'found citation'
});
expect(results[0]).toHaveFixMatching([
{start: 50, end: 80, replacement: `Q.CITE = 'SOME EXAMPLE STRING'`}
]);
});
it('for several matches', () => {
const rule = new ConformancePatternRule(baseConfig, uppercaseFixer);
const sourceTwoMatches =
source + `q.cite = 'some other example string';\n`;
const results = compileAndCheck(rule, sourceTwoMatches);
expect(results).toHaveNFailures(2, baseConfig);
expect(results[0]).toBeFailureMatching({
matchedCode: `q.cite = 'some example string'`,
messageText: 'found citation'
});
expect(results[1]).toBeFailureMatching({
matchedCode: `q.cite = 'some other example string'`,
messageText: 'found citation'
});
expect(results[0]).toHaveFixMatching([
{start: 50, end: 80, replacement: `Q.CITE = 'SOME EXAMPLE STRING'`}
]);
expect(results[0].fixToReadableStringInContext())
.toBe(
`Suggested fix:\n` +
`- Replace the full match with: Q.CITE = 'SOME EXAMPLE STRING'`);
expect(results[1]).toHaveFixMatching([{
start: 82,
end: 118,
replacement: `Q.CITE = 'SOME OTHER EXAMPLE STRING'`
}]);
expect(results[1].fixToReadableStringInContext())
.toBe(
`Suggested fix:\n` +
`- Replace the full match with: Q.CITE = 'SOME OTHER EXAMPLE STRING'`);
});
});
describe('adds imports', () => {
const addNamedImportFixer: Fixer = {
getFixForFlaggedNode(n: ts.Node) {
const ic =
maybeAddNamedImport(n.getSourceFile(), 'foo', './file_1', 'bar');
if (ic) return {changes: [ic]};
return;
}
};
it('maybeAddNamedImport additions', () => {
const results = compileAndCheck(
new ConformancePatternRule(baseConfig, addNamedImportFixer), source);
expect(results[0]).toHaveFixMatching([{
start: 0,
end: 0,
replacement: `import {foo as bar} from './file_1';\n`
}]);
expect(results[0].fixToReadableStringInContext())
.toBe(
`Suggested fix:\n` +
`- Add new import: import {foo as bar} from './file_1';`);
});
it('maybeAddNamedImport already there', () => {
const results = compileAndCheck(
new ConformancePatternRule(baseConfig, addNamedImportFixer),
'import {foo as bar} from \'./file_1\';\n' + source,
'export const foo = 1;');
expect(results[0]).toHaveNoFix();
expect(results[0].fixToReadableStringInContext()).toBe('');
});
it('maybeAddNamedImport different name', () => {
const results = compileAndCheck(
new ConformancePatternRule(baseConfig, addNamedImportFixer),
'import {foo as baz} from \'./file_1\';\n' + source,
'export const foo = 1;');
expect(results[0]).toHaveFixMatching([
{start: 8, end: 8, replacement: `foo as bar, `}
]);
expect(results[0].fixToReadableStringInContext())
.toBe(
`Suggested fix:\n` +
`- Insert at line 1, char 9: foo as bar,`);
});
it('maybeAddNamespacedImport', () => {
const addNamespacedImportFixer: Fixer = {
getFixForFlaggedNode(n: ts.Node): Fix |
undefined {
const ic =
maybeAddNamespaceImport(n.getSourceFile(), './file_1', 'foo');
if (ic) return {changes: [ic]};
return;
}
};
const results = compileAndCheck(
new ConformancePatternRule(baseConfig, addNamespacedImportFixer),
source);
expect(results[0]).toHaveFixMatching([
{start: 0, end: 0, replacement: `import * as foo from './file_1';\n`}
]);
});
});
describe('the logic for location->text transforms', () => {
const sourceFile = compile(`let a;\nlet b;\n`)
.getSourceFiles()
.filter(f => f.fileName.indexOf('file_0') !== -1)[0];
// let a;\nlet b;\n
// 0123456 7890123 Positions
// 1234567 1234567 Expected result in characters
it('stringifies as expected', () => {
// Only the sourceFile matters here.
const failure = new Failure(sourceFile, NaN, NaN, 'whatever', NaN);
expect(failure.readableRange(0, 0)).toBe('at line 1, char 1');
expect(failure.readableRange(1, 1)).toBe('at line 1, char 2');
expect(failure.readableRange(0, 1)).toBe('line 1, from char 1 to 2');
expect(failure.readableRange(0, 1)).toBe('line 1, from char 1 to 2');
expect(failure.readableRange(7, 7)).toBe('at line 2, char 1');
expect(failure.readableRange(0, 7))
.toBe('from line 1, char 1 to line 2, char 1');
});
});
});
beforeEach(() => {
jasmine.addMatchers(customMatchers);
});