Skip to content

Commit

Permalink
chore(no-identical-title): migrate to TS (#348)
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath authored and SimenB committed Jul 24, 2019
1 parent d218d64 commit 0a0f20e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 194 deletions.
@@ -1,7 +1,11 @@
import { RuleTester } from 'eslint';
import { TSESLint } from '@typescript-eslint/experimental-utils';
import rule from '../no-identical-title';

const ruleTester = new RuleTester();
const ruleTester = new TSESLint.RuleTester({
parserOptions: {
ecmaVersion: 6,
},
});

ruleTester.run('no-identical-title', rule, {
valid: [
Expand Down Expand Up @@ -58,21 +62,8 @@ ruleTester.run('no-identical-title', rule, {
'describe("describe2", function() {});',
].join('\n'),
['it("it" + n, function() {});', 'it("it" + n, function() {});'].join('\n'),
{
code: [
'it(`it${n}`, function() {});',
'it(`it${n}`, function() {});',
].join('\n'),
env: {
es6: true,
},
},
{
code: 'it(`${n}`, function() {});',
env: {
es6: true,
},
},
['it(`it${n}`, function() {});', 'it(`it${n}`, function() {});'].join('\n'),
'it(`${n}`, function() {});',
[
'describe("title " + foo, function() {',
' describe("describe1", function() {});',
Expand All @@ -87,54 +78,28 @@ ruleTester.run('no-identical-title', rule, {
' });',
'});',
].join('\n'),
{
code: [
'describe("describe", () => {',
' it(`testing ${someVar} with the same title`, () => {});',
' it(`testing ${someVar} with the same title`, () => {});',
'});',
].join('\n'),
env: {
es6: true,
},
},
{
code: ['it(`it1`, () => {});', 'it(`it2`, () => {});'].join('\n'),
env: {
es6: true,
},
},
{
code: [
'describe(`describe1`, () => {});',
'describe(`describe2`, () => {});',
].join('\n'),
env: {
es6: true,
},
},
{
code: [
'const test = { content: () => "foo" }',
'test.content(`testing backticks with the same title`);',
'test.content(`testing backticks with the same title`);',
].join('\n'),
env: {
es6: true,
},
},
{
code: [
'const describe = { content: () => "foo" }',
'describe.content(`testing backticks with the same title`);',
'describe.content(`testing backticks with the same title`);',
].join('\n'),
env: {
es6: true,
},
},
[
'describe("describe", () => {',
' it(`testing ${someVar} with the same title`, () => {});',
' it(`testing ${someVar} with the same title`, () => {});',
'});',
].join('\n'),
['it(`it1`, () => {});', 'it(`it2`, () => {});'].join('\n'),
[
'describe(`describe1`, () => {});',
'describe(`describe2`, () => {});',
].join('\n'),
[
'const test = { content: () => "foo" }',
'test.content(`testing backticks with the same title`);',
'test.content(`testing backticks with the same title`);',
].join('\n'),
[
'const describe = { content: () => "foo" }',
'describe.content(`testing backticks with the same title`);',
'describe.content(`testing backticks with the same title`);',
].join('\n'),
],

invalid: [
{
code: [
Expand Down Expand Up @@ -208,9 +173,6 @@ ruleTester.run('no-identical-title', rule, {
' it(`testing backticks with the same title`, () => {});',
'});',
].join('\n'),
env: {
es6: true,
},
errors: [{ messageId: 'multipleTestTitle', column: 5, line: 3 }],
},
],
Expand Down
85 changes: 0 additions & 85 deletions src/rules/no-identical-title.js

This file was deleted.

77 changes: 77 additions & 0 deletions src/rules/no-identical-title.ts
@@ -0,0 +1,77 @@
import {
createRule,
getStringValue,
hasExpressions,
isDescribe,
isStringNode,
isTemplateLiteral,
isTestCase,
} from './tsUtils';

interface DescribeContext {
describeTitles: string[];
testTitles: String[];
}

const newDescribeContext = (): DescribeContext => ({
describeTitles: [],
testTitles: [],
});

export default createRule({
name: __filename,
meta: {
docs: {
category: 'Best Practices',
description: 'Disallow identical titles',
recommended: 'error',
},
messages: {
multipleTestTitle:
'Test title is used multiple times in the same describe block.',
multipleDescribeTitle:
'Describe block title is used multiple times in the same describe block.',
},
schema: [],
type: 'suggestion',
},
defaultOptions: [],
create(context) {
const contexts = [newDescribeContext()];
return {
CallExpression(node) {
const currentLayer = contexts[contexts.length - 1];
if (isDescribe(node)) {
contexts.push(newDescribeContext());
}
const [firstArgument] = node.arguments;
if (
!isStringNode(firstArgument) ||
(isTemplateLiteral(firstArgument) && hasExpressions(firstArgument))
) {
return;
}
const title = getStringValue(firstArgument);
if (isTestCase(node)) {
if (currentLayer.testTitles.includes(title)) {
context.report({ messageId: 'multipleTestTitle', node });
}
currentLayer.testTitles.push(title);
}

if (!isDescribe(node)) {
return;
}
if (currentLayer.describeTitles.includes(title)) {
context.report({ messageId: 'multipleDescribeTitle', node });
}
currentLayer.describeTitles.push(title);
},
'CallExpression:exit'(node) {
if (isDescribe(node)) {
contexts.pop();
}
},
};
},
});
43 changes: 1 addition & 42 deletions src/rules/util.js
Expand Up @@ -97,53 +97,12 @@ export const argument = node =>
export const argument2 = node =>
node.parent.parent.parent.arguments && node.parent.parent.parent.arguments[0];

const describeAliases = new Set(['describe', 'fdescribe', 'xdescribe']);

const testCaseNames = new Set(['fit', 'it', 'test', 'xit', 'xtest']);

const describeProperties = new Set(['each', 'only', 'skip']);

const testCaseProperties = new Set(['each', 'only', 'skip', 'todo']);

export const isTestCase = node =>
node &&
node.type === 'CallExpression' &&
((node.callee.type === 'Identifier' && testCaseNames.has(node.callee.name)) ||
(node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
testCaseNames.has(node.callee.object.name) &&
node.callee.property.type === 'Identifier' &&
testCaseProperties.has(node.callee.property.name)));

export const isDescribe = node =>
node &&
node.type === 'CallExpression' &&
((node.callee.type === 'Identifier' &&
describeAliases.has(node.callee.name)) ||
(node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
describeAliases.has(node.callee.object.name) &&
node.callee.property.type === 'Identifier' &&
describeProperties.has(node.callee.property.name)));

export const isFunction = node =>
node &&
(node.type === 'FunctionExpression' ||
node.type === 'ArrowFunctionExpression');

export const isString = node =>
node &&
((node.type === 'Literal' && typeof node.value === 'string') ||
isTemplateLiteral(node));

export const isTemplateLiteral = node =>
node && node.type === 'TemplateLiteral';

export const hasExpressions = node =>
node && node.expressions && node.expressions.length > 0;

export const getStringValue = arg =>
isTemplateLiteral(arg) ? arg.quasis[0].value.raw : arg.value;
export const getStringValue = arg => arg.quasis[0].value.raw;

/**
* Generates the URL to documentation for the given rule name. It uses the
Expand Down

0 comments on commit 0a0f20e

Please sign in to comment.