diff --git a/README.md b/README.md index a050f44..8905f22 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Smelly test is an extension that helps developers mitigate test smells in their ## Features -- Identify if statements in the test code +- Identify **if** statements in the test code +- Identify **for of** loops in the test code ## Available at diff --git a/vscode/src/modules/smells-finder/languages/TypescriptSmells.ts b/vscode/src/modules/smells-finder/languages/TypescriptSmells.ts index 7a51459..1cf1a1b 100644 --- a/vscode/src/modules/smells-finder/languages/TypescriptSmells.ts +++ b/vscode/src/modules/smells-finder/languages/TypescriptSmells.ts @@ -12,7 +12,19 @@ export class TypescriptSmells implements SmellsFinder { const ast = ts.createSourceFile('temp.ts', this.code, ts.ScriptTarget.ES2020, true); const nodes = this.findIfStatements(ast); - return nodes.map(ifStmt => { + const forOfs = this.findForOfStatements(ast).map(forStmt => { + const { line: startLine, character } = ts.getLineAndCharacterOfPosition(ast, forStmt.getStart()); + const { line: endLine, character: endCharacter } = ts.getLineAndCharacterOfPosition(ast, forStmt.getEnd()); + + return SmellsBuilder.forOfStatement( + startLine + 1, + endLine + 1, + character, + endCharacter, + ); + }); + + const ifs = nodes.map(ifStmt => { const { line: startLine, character } = ts.getLineAndCharacterOfPosition(ast, ifStmt.getStart()); const { line: endLine, character: endCharacter } = ts.getLineAndCharacterOfPosition(ast, ifStmt.getEnd()); @@ -23,9 +35,11 @@ export class TypescriptSmells implements SmellsFinder { endCharacter, ); }); + + return ifs.concat(forOfs); } - findIfStatements(node: ts.Node, ifStatements: ts.IfStatement[] = []): ts.IfStatement[] { + private findIfStatements(node: ts.Node, ifStatements: ts.IfStatement[] = []): ts.IfStatement[] { if (ts.isIfStatement(node)) { ifStatements.push(node); } @@ -36,4 +50,16 @@ export class TypescriptSmells implements SmellsFinder { return ifStatements; } + + private findForOfStatements(node: ts.Node, forOfStatements: ts.ForOfStatement[] = []): ts.ForOfStatement[] { + if (ts.isForOfStatement(node)) { + forOfStatements.push(node); + } + + node.forEachChild(child => { + this.findForOfStatements(child, forOfStatements); + }); + + return forOfStatements; + } } diff --git a/vscode/src/modules/smells-finder/test/smells-detector.test.ts b/vscode/src/modules/smells-finder/test/smells-detector.test.ts index 08d6d85..4ee6314 100644 --- a/vscode/src/modules/smells-finder/test/smells-detector.test.ts +++ b/vscode/src/modules/smells-finder/test/smells-detector.test.ts @@ -2,16 +2,22 @@ import * as assert from 'assert'; import { suite, test } from 'mocha'; import { SmellDetector } from '../smells-detector'; +const IF_STATEMENT = 'if-statement'; +const FOR_OF = 'for-of-statement'; + +const JAVASCRIPT = 'javascript'; +const TYPESCRIPT = 'typescript'; + suite('Smelly Extension Test Suite', () => { suite('Javascript', () => { test('find if in the test code', () => { const code = `const a = 1; if (a === 1) {}`; - const smellDetector = new SmellDetector(code, 'javascript'); + const smellDetector = new SmellDetector(code, JAVASCRIPT); const result = smellDetector.findAll(); - assert.equal(result[0].type, 'if-statement'); + assert.equal(result[0].type, IF_STATEMENT); assert.equal(result[0].lineStart, 2); assert.equal(result[0].lineEnd, 2); assert.equal(result[0].startAt, 0); @@ -25,10 +31,10 @@ for (const i of lists) { }`; - const smellDetector = new SmellDetector(code, 'javascript'); + const smellDetector = new SmellDetector(code, JAVASCRIPT); const result = smellDetector.findAll(); - assert.equal(result[0].type, 'for-of-statement'); + assert.equal(result[0].type,FOR_OF); assert.equal(result[0].lineStart, 3); assert.equal(result[0].lineEnd, 5); assert.equal(result[0].startAt, 0); @@ -41,10 +47,10 @@ for (const i of lists) { const code = `const a: number = 1; if (a === 1) { }`; - const smellDetector = new SmellDetector(code, 'typescript'); + const smellDetector = new SmellDetector(code, TYPESCRIPT); const result = smellDetector.findAll(); - assert.equal(result[0].type, 'if-statement'); + assert.equal(result[0].type, IF_STATEMENT); assert.equal(result[0].lineStart, 2, 'line start'); assert.equal(result[0].lineEnd, 2, 'line end'); assert.equal(result[0].startAt, 0, 'start at'); @@ -58,7 +64,7 @@ if (a === 2) { console.log('this is a test'); }`; - const smellDetector = new SmellDetector(code, 'typescript'); + const smellDetector = new SmellDetector(code, TYPESCRIPT); const result = smellDetector.findAll(); assert.equal(result.length, 2); @@ -71,14 +77,31 @@ if (a === 2) { console.log('this is a test'); }`; - const smellDetector = new SmellDetector(code, 'typescript'); + const smellDetector = new SmellDetector(code, TYPESCRIPT); const result = smellDetector.findAll(); - assert.equal(result[1].type, 'if-statement'); + assert.equal(result[1].type, IF_STATEMENT); assert.equal(result[1].lineStart, 3, 'line start'); assert.equal(result[1].lineEnd, 5, 'line end'); assert.equal(result[1].startAt, 0, 'start at'); assert.equal(result[1].endsAt, 1, 'end at'); }); + + test('find for in the test code', () => { + const code = `const lists: any[] = [{}, {}]; + +for (const i of lists) { + +}`; + + const smellDetector = new SmellDetector(code, TYPESCRIPT); + const result = smellDetector.findAll(); + + assert.equal(result[0].type,FOR_OF); + assert.equal(result[0].lineStart, 3); + assert.equal(result[0].lineEnd, 5); + assert.equal(result[0].startAt, 0); + assert.equal(result[0].endsAt, 1); + }); }); }); diff --git a/vscode/src/modules/vscode/test/suite/extension.test.ts b/vscode/src/modules/vscode/test/suite/extension.test.ts index ee9a105..d57353e 100644 --- a/vscode/src/modules/vscode/test/suite/extension.test.ts +++ b/vscode/src/modules/vscode/test/suite/extension.test.ts @@ -2,15 +2,25 @@ import * as assert from 'assert'; import * as path from 'path'; import * as vscode from 'vscode'; -const testFolderLocationForJavascript = '../../../../../../src/modules/vscode/dataset/javascript'; -const testFolderLocationForTypescript = '../../../../../../src/modules/vscode/dataset/typescript'; +function fileForJavascript(file: string) { + const testFolderLocationForJavascript = '../../../../../../src/modules/vscode/dataset/javascript'; + return testFolderLocationForJavascript + '/' + file; +} + +function fileFortypescript(file: string) { + const testFolderLocationForTypescript = '../../../../../../src/modules/vscode/dataset/typescript'; + return testFolderLocationForTypescript + '/' + file; +} suite('Smelly Extension Test Suite', () => { [ - { language: 'javascript', file: testFolderLocationForJavascript + '/script_with_if.test.js', expectedTestSmell: 1 }, - { language: 'javascript', file: testFolderLocationForJavascript + '/real_test_with_if.test.js', expectedTestSmell: 7 }, - { language: 'javascript', file: testFolderLocationForJavascript + '/script_with_for.test.js', expectedTestSmell: 1 }, - { language: 'typescript', file: testFolderLocationForTypescript + '/script_with_if.test.ts', expectedTestSmell: 1 }, + { language: 'javascript', file: fileForJavascript('/script_with_if.test.js'), expectedTestSmell: 1 }, + { language: 'javascript', file: fileForJavascript('real_test_with_if.test.js'), expectedTestSmell: 7 }, + { language: 'javascript', file: fileForJavascript('script_with_for.test.js'), expectedTestSmell: 1 }, + // typescript + { language: 'typescript', file: fileFortypescript('script_with_if.test.ts'), expectedTestSmell: 1 }, + { language: 'typescript', file: fileFortypescript('script_with_for.test.ts'), expectedTestSmell: 1 }, + // typescript ].forEach(({ language, file, expectedTestSmell }) => { test(`Shows smelly in diagnostics panel, language: ${language}, file: ${file}, expected smells: ${expectedTestSmell}`, async () => { const currentFile = path.join(__dirname + file); @@ -31,7 +41,7 @@ suite('Smelly Extension Test Suite', () => { }); test('ignores files that are not for test', async () => { - const file = testFolderLocationForJavascript + '/script_with_if.js'; + const file = fileForJavascript('script_with_if.js'); const language = 'javascript'; const currentFile = path.join(__dirname + file);