Skip to content

Commit 3f9ba28

Browse files
committed
feat(detector): shows line and smells details in the html report
1 parent 1601186 commit 3f9ba28

File tree

2 files changed

+142
-76
lines changed

2 files changed

+142
-76
lines changed

detector/src/reporters/layout/example.html

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"
6565
</div>
6666
</div>
6767
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
68-
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
68+
<table data-testid="file-list" class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
6969
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
7070
<tr>
7171
<th scope="col" class="px-6 py-3">
@@ -96,9 +96,32 @@ <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"
9696
</tr>
9797
<tr class="hidden" id="details-{{@index}}">
9898
<td colspan="3" class="p-0" data-testid="{{@index}}-code">
99-
{{#each smells}}
100-
<div data-testid="{{@../index}}-smell-name-{{@index}}">{{this.type}}</div>
101-
{{/each }}
99+
{{#if smells.length}}
100+
<table>
101+
<thead>
102+
<tr>
103+
<th data-testid="smells-table-type">Smell type</th>
104+
<th data-testid="smells-table-description">Smell description</th>
105+
<th data-testid="smells-table-start-line">Smell line</th>
106+
</tr>
107+
</thead>
108+
<tbody>
109+
<tr>
110+
{{#each smells}}
111+
<td>
112+
<div data-testid="{{@../index}}-smell-name-{{@index}}">{{this.type}}</div>
113+
</td>
114+
<td>
115+
<div data-testid="{{@../index}}-smell-description-{{@index}}">{{this.description}}</div>
116+
</td>
117+
<td>
118+
<div data-testid="{{@../index}}-smell-start-line-{{@index}}">{{this.lineStart}}</div>
119+
</td>
120+
{{/each }}
121+
</tr>
122+
</tbody>
123+
</table>
124+
{{/if}}
102125
<pre><code>{{this.fileContent}}</code></pre>
103126
</td>
104127
</tr>

detector/test/html-report.integration.test.ts

Lines changed: 115 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,49 @@ import { buildEmptyHtmlReportForTestSmells, buildHtmlReportForTestSmellsFor } fr
66
import { SmellsBuilder } from '../src/smells-builder';
77
import { Smell, SmellsList, SupportedLanguages } from '../src/types';
88

9+
const oneFileWithFourConsoleSmells = (): AggregatedData => {
10+
const smell: Smell = SmellsBuilder.console(0, 1, 10, 20);
11+
const smellsFound: SmellsList[] = [
12+
{
13+
smells: [smell, smell, smell, smell],
14+
language: SupportedLanguages.javascript,
15+
fileName: 'first_test.js',
16+
fileContent: 'console.log("Hello world")',
17+
},
18+
];
19+
20+
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
21+
};
22+
23+
const oneFileWithOneConsoleSmells = (): AggregatedData => {
24+
const smellsFound: SmellsList[] = [
25+
{
26+
smells: [
27+
SmellsBuilder.console(0, 1, 10, 20),
28+
SmellsBuilder.forOfStatement(0, 2, 11, 22),
29+
],
30+
language: SupportedLanguages.javascript,
31+
fileName: 'first_test.js',
32+
fileContent: 'console.log("Hello world")',
33+
},
34+
];
35+
36+
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
37+
};
38+
39+
const emptySmellsListForSingleFile = (): AggregatedData => {
40+
const smellsFound: SmellsList[] = [
41+
{
42+
smells: [],
43+
language: SupportedLanguages.javascript,
44+
fileName: 'first_test.js',
45+
fileContent: 'console.log("Hello world")',
46+
},
47+
];
48+
49+
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
50+
};
51+
952
describe('html report', () => {
1053
const exportsOptions: ExportOptions = { to: '.' };
1154
const filePath = `${exportsOptions.to}/smelly-report.html`;
@@ -19,14 +62,6 @@ describe('html report', () => {
1962
});
2063

2164
describe('when no smells are found', () => {
22-
23-
test('renders empty table when no tests are found', async () => {
24-
const generatedHtml = await buildEmptyHtmlReportForTestSmells(exportsOptions, filePath);
25-
const root = parse(generatedHtml);
26-
27-
expect(root.querySelectorAll('table tbody tr').length).toEqual(0);
28-
});
29-
3065
test('renders report title', async () => {
3166
const generatedHtml = await buildEmptyHtmlReportForTestSmells(exportsOptions, filePath);
3267
const root = parse(generatedHtml);
@@ -60,91 +95,99 @@ describe('html report', () => {
6095
});
6196

6297
describe('when there are test smells', () => {
63-
const oneFileWithFourConsoleSmells = (): AggregatedData => {
64-
const smell: Smell = SmellsBuilder.console(0, 1, 10, 20);
65-
const smellsFound: SmellsList[] = [
66-
{
67-
smells: [smell, smell, smell, smell],
68-
language: SupportedLanguages.javascript,
69-
fileName: 'first_test.js',
70-
fileContent: 'console.log("Hello world")',
71-
},
72-
];
73-
74-
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
75-
};
76-
7798
test('renders number of test smell for a given file', async () => {
7899
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
79100
const root = parse(generatedHtml);
80101

81-
expect(root.querySelector('table tbody tr')?.textContent).toContain('4');
102+
expect(root.querySelector('[data-testid="file-list"] tbody tr')?.textContent).toContain('4');
82103
});
83104

84105
test('renders file name', async () => {
85106
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
86107
const root = parse(generatedHtml);
87108

88-
expect(root.querySelector('table tbody tr')?.textContent).toContain('first_test.js');
109+
expect(root.querySelector('[data-testid="file-list"] tbody tr')?.textContent).toContain('first_test.js');
89110
});
90111

91-
test('renders smell for file for one smell', async () => {
92-
const oneFileWithOneConsoleSmells = (): AggregatedData => {
93-
const smell: Smell = SmellsBuilder.console(0, 1, 10, 20);
94-
const smellsFound: SmellsList[] = [
95-
{
96-
smells: [smell],
97-
language: SupportedLanguages.javascript,
98-
fileName: 'first_test.js',
99-
fileContent: 'console.log("Hello world")',
100-
},
101-
];
102-
103-
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
104-
};
105-
const aggregatedData = oneFileWithOneConsoleSmells();
106-
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
112+
test('renders language', async () => {
113+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
107114
const root = parse(generatedHtml);
108115

109-
expect(root.querySelector('[data-testid="0-smell-name-0"]')?.textContent).toContain('console-statement');
116+
expect(root.querySelector('[data-testid="file-list"] tbody tr')?.textContent).toContain('javascript');
110117
});
111-
112-
test('renders smell for file with two smells', async () => {
113-
const oneFileWithOneConsoleSmells = (): AggregatedData => {
114-
const smellsFound: SmellsList[] = [
115-
{
116-
smells: [
117-
SmellsBuilder.console(0, 1, 10, 20),
118-
SmellsBuilder.forOfStatement(0, 2, 11, 22),
119-
],
120-
language: SupportedLanguages.javascript,
121-
fileName: 'first_test.js',
122-
fileContent: 'console.log("Hello world")',
123-
},
124-
];
125-
126-
return { data: smellsFound, totalSmells: 0, averageSmellsPerTestFile: 0, totalTestCases: 0 };
127-
};
128-
const aggregatedData = oneFileWithOneConsoleSmells();
129-
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
130-
const root = parse(generatedHtml);
131118

132-
expect(root.querySelector('[data-testid="0-smell-name-1"]')?.textContent).toContain('for-of-statement');
119+
describe('smells table with smells', () => {
120+
test('renders info about no smells', async () => {
121+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, emptySmellsListForSingleFile());
122+
const root = parse(generatedHtml);
123+
124+
expect(root.querySelectorAll('[data-testid="smells-table-type"]')).toHaveLength(0);
125+
});
133126
});
134127

135-
test('renders test file code', async () => {
136-
const aggregatedData = oneFileWithFourConsoleSmells();
137-
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
138-
const root = parse(generatedHtml);
128+
describe('smells table with smells', () => {
129+
test('renders smells type', async () => {
130+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
131+
const root = parse(generatedHtml);
139132

140-
expect(root.querySelector('[data-testid="0-code"]')?.textContent).toContain('console.log("Hello world")');
141-
});
133+
expect(root.querySelector('[data-testid="smells-table-type"]')?.textContent).toContain('Smell type');
134+
});
142135

143-
test('renders language', async () => {
144-
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
145-
const root = parse(generatedHtml);
136+
test('renders smells description', async () => {
137+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
138+
const root = parse(generatedHtml);
139+
140+
expect(root.querySelector('[data-testid="smells-table-description"]')?.textContent).toContain('Smell description');
141+
});
142+
143+
test('renders smells start line', async () => {
144+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, oneFileWithFourConsoleSmells());
145+
const root = parse(generatedHtml);
146+
147+
expect(root.querySelector('[data-testid="smells-table-start-line"]')?.textContent).toContain('Smell line');
148+
});
149+
150+
describe('smells detail', () => {
151+
test('renders smell for file for one smell', async () => {
152+
const aggregatedData = oneFileWithOneConsoleSmells();
153+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
154+
const root = parse(generatedHtml);
155+
156+
expect(root.querySelector('[data-testid="0-smell-name-0"]')?.textContent).toContain('console-statement');
157+
});
158+
159+
test('renders smell description', async () => {
160+
const aggregatedData = oneFileWithOneConsoleSmells();
161+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
162+
const root = parse(generatedHtml);
163+
164+
expect(root.querySelector('[data-testid="0-smell-description-0"]')?.textContent).toContain('Smelly: Avoid poluting the test output. It is known as the loudmouth');
165+
});
166+
167+
test('renders smell start line', async () => {
168+
const aggregatedData = oneFileWithOneConsoleSmells();
169+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
170+
const root = parse(generatedHtml);
171+
172+
expect(root.querySelector('[data-testid="0-smell-start-line-0"]')?.textContent).toContain('0');
173+
});
174+
175+
test('renders smell for file with two smells', async () => {
176+
const aggregatedData = oneFileWithOneConsoleSmells();
177+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
178+
const root = parse(generatedHtml);
179+
180+
expect(root.querySelector('[data-testid="0-smell-name-1"]')?.textContent).toContain('for-of-statement');
181+
});
182+
183+
test('renders test file code', async () => {
184+
const aggregatedData = oneFileWithFourConsoleSmells();
185+
const generatedHtml = await buildHtmlReportForTestSmellsFor(exportsOptions, filePath, aggregatedData);
186+
const root = parse(generatedHtml);
146187

147-
expect(root.querySelector('table tbody tr')?.textContent).toContain('javascript');
188+
expect(root.querySelector('[data-testid="0-code"]')?.textContent).toContain('console.log("Hello world")');
189+
});
190+
});
148191
});
149192
});
150193
});

0 commit comments

Comments
 (0)