ortoni-report 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Change Log:
2
2
 
3
+ ## Version 2.0.2
4
+
5
+ #### 🚀 New Features
6
+
7
+ - **Show or Hide Projects in Test List**
8
+ - You can now show or hide specific projects in the test list.
9
+ - ```showProject: true``` from config
10
+
11
+ - **Document Title from Config**
12
+ - The document title is now configurable via the configuration file, allowing you to customize it to your preference.
13
+
14
+ - **Display Tags in Test Section**
15
+ - Tags associated with tests are now displayed within the test section, giving you a clearer overview of test categories.
16
+
17
+ - **Project and Tags Added to Filter**
18
+ - We've added the ability to filter tests by both project and tags, enhancing the granularity of your test views.
19
+
20
+ - **Display Selected Status on UI**
21
+ - The selected status filter is now visible on the UI, making it easier to track the current filter settings.
22
+
23
+ #### 🛠 Fixes
24
+
25
+ - **Project Drop-down Z-Index on Screenshot**
26
+ - Resolved an issue where the project drop-down menu was being overlapped by screenshots. The z-index has been adjusted for proper layering.
27
+
28
+ - **Project Filter Hide Test Steps**
29
+ - Fixed a bug where applying the project filter would inadvertently hide test steps. Test steps are now correctly displayed based on the filter.
30
+
31
+ #### ✨ Improvements
32
+
33
+ - **Hide Skipped Tests on All Tests Filter**
34
+ - Skipped tests are now hidden by default when using the "All Tests" filter, providing a cleaner and more focused view of relevant tests.
35
+
36
+ - **Colorful Dashboard**
37
+ - We've enhanced the visual appeal of the dashboard with more vibrant and intuitive colors, making it easier to navigate and interpret results.
38
+
39
+
40
+ ## Version 2.0.1
41
+ - Fixed local and root path issue of Parcel bundler.
42
+ - Local config issue
43
+
3
44
  ## Version 2.0.0
4
45
  - Fixed local and root path issue of Parcel bundler.
5
46
 
package/dist/cli/cli.js CHANGED
@@ -9,6 +9,11 @@ const path_1 = __importDefault(require("path"));
9
9
  const commander_1 = require("commander");
10
10
  const child_process_1 = require("child_process");
11
11
  const utils_1 = require("../utils/utils");
12
+ const findParcelBinary = () => {
13
+ const localParcel = path_1.default.resolve(__dirname, '../../node_modules/.bin/parcel');
14
+ const projectParcel = path_1.default.resolve(process.cwd(), 'node_modules/.bin/parcel');
15
+ return fs_1.default.existsSync(localParcel) ? localParcel : projectParcel;
16
+ };
12
17
  commander_1.program
13
18
  .name('ortoni-report')
14
19
  .description('Ortoni Report by LetCode with Koushik')
@@ -25,7 +30,7 @@ commander_1.program
25
30
  process.exit(1);
26
31
  }
27
32
  // Resolve the path to the local parcel binary
28
- const parcelPath = path_1.default.resolve(__dirname, '../../node_modules/.bin/parcel');
33
+ const parcelPath = findParcelBinary();
29
34
  const parcelCommand = `${parcelPath} build ${reportPath} --dist-dir ortoni-report --public-url ./`;
30
35
  console.log('Bundling Ortoni Report...');
31
36
  (0, child_process_1.exec)(parcelCommand, (error, stdout, stderr) => {
package/dist/css/main.css CHANGED
@@ -1,6 +1,5 @@
1
1
  @charset "UTF-8";
2
2
  /* Bulma Utilities */
3
- @import url("https://fonts.googleapis.com/css?family=Nunito:400,700");
4
3
  :root {
5
4
  --bulma-control-radius: var(--bulma-radius);
6
5
  --bulma-control-radius-small: var(--bulma-radius-small);
@@ -5710,7 +5709,7 @@ button.tag:active,
5710
5709
  }
5711
5710
  .select:not(.is-multiple):not(.is-loading)::after {
5712
5711
  inset-inline-end: 1.125em;
5713
- z-index: 4;
5712
+ z-index: 0;
5714
5713
  }
5715
5714
  .select.is-rounded select {
5716
5715
  border-radius: var(--bulma-radius-rounded);
@@ -4,6 +4,16 @@ import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@
4
4
  * Configuration options for OrtoniReport.
5
5
  */
6
6
  interface OrtoniReportConfig {
7
+ /**
8
+ * The title of the HTML report.
9
+ * @example "Ortoni Playwright Test Report"
10
+ */
11
+ title?: string;
12
+ /**
13
+ * Add project on the list of the tests? (Filtering projects still works if hidden)
14
+ * @example true to display, false to hide.
15
+ */
16
+ showProject?: boolean;
7
17
  /**
8
18
  * The name of the project.
9
19
  * @example "Ortoni Project"
@@ -67,7 +77,7 @@ interface TestResultData {
67
77
  logs: string;
68
78
  screenshotPath?: string | null | undefined;
69
79
  filePath: string;
70
- projects: Set<string>;
80
+ filters: Set<string>;
71
81
  tracePath?: string;
72
82
  videoPath?: string;
73
83
  base64Image: boolean | undefined;
@@ -6671,7 +6671,7 @@ var OrtoniReport = class {
6671
6671
  result.stdout.concat(result.stderr).map((log) => log).join("\n")
6672
6672
  ),
6673
6673
  filePath,
6674
- projects: this.projectSet,
6674
+ filters: this.projectSet,
6675
6675
  base64Image: this.config.base64Image
6676
6676
  };
6677
6677
  this.attachFiles(result, testResult);
@@ -6721,12 +6721,15 @@ var OrtoniReport = class {
6721
6721
  );
6722
6722
  const totalDuration = msToTime(result.duration);
6723
6723
  this.groupResults();
6724
+ import_handlebars.default.registerHelper("joinWithSpace", (array) => array.join(" "));
6724
6725
  import_handlebars.default.registerHelper("json", (context) => safeStringify(context));
6725
6726
  import_handlebars.default.registerHelper(
6726
6727
  "eq",
6727
6728
  (actualStatus, expectedStatus) => actualStatus === expectedStatus
6728
6729
  );
6729
- const outputFilename = ensureHtmlExtension(this.config.filename || "ortoni-report.html");
6730
+ const outputFilename = ensureHtmlExtension(
6731
+ this.config.filename || "ortoni-report.html"
6732
+ );
6730
6733
  const html = this.generateHTML(filteredResults, totalDuration);
6731
6734
  const outputPath = import_path2.default.resolve(process.cwd(), outputFilename);
6732
6735
  import_fs.default.writeFileSync(outputPath, html);
@@ -6736,14 +6739,24 @@ var OrtoniReport = class {
6736
6739
  }
6737
6740
  }
6738
6741
  groupResults() {
6739
- this.groupedResults = this.results.reduce((acc, result, index) => {
6740
- const { filePath, suite, projectName } = result;
6741
- acc[filePath] = acc[filePath] || {};
6742
- acc[filePath][suite] = acc[filePath][suite] || {};
6743
- acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
6744
- acc[filePath][suite][projectName].push({ ...result, index });
6745
- return acc;
6746
- }, {});
6742
+ if (this.config.showProject) {
6743
+ this.groupedResults = this.results.reduce((acc, result, index) => {
6744
+ const { filePath, suite, projectName } = result;
6745
+ acc[filePath] = acc[filePath] || {};
6746
+ acc[filePath][suite] = acc[filePath][suite] || {};
6747
+ acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
6748
+ acc[filePath][suite][projectName].push({ ...result, index });
6749
+ return acc;
6750
+ }, {});
6751
+ } else {
6752
+ this.groupedResults = this.results.reduce((acc, result, index) => {
6753
+ const { filePath, suite } = result;
6754
+ acc[filePath] = acc[filePath] || {};
6755
+ acc[filePath][suite] = acc[filePath][suite] || [];
6756
+ acc[filePath][suite].push({ ...result, index });
6757
+ return acc;
6758
+ }, {});
6759
+ }
6747
6760
  }
6748
6761
  generateHTML(filteredResults, totalDuration) {
6749
6762
  try {
@@ -6762,6 +6775,11 @@ var OrtoniReport = class {
6762
6775
  );
6763
6776
  const template = import_handlebars.default.compile(templateSource);
6764
6777
  const logo = this.config.logo ? import_path2.default.resolve(this.config.logo) : void 0;
6778
+ const allTags = /* @__PURE__ */ new Set();
6779
+ this.results.forEach((result) => {
6780
+ result.suiteTags.forEach((tag) => allTags.add(tag));
6781
+ result.testTags.forEach((tag) => allTags.add(tag));
6782
+ });
6765
6783
  const data = {
6766
6784
  logo,
6767
6785
  totalDuration,
@@ -6780,7 +6798,10 @@ var OrtoniReport = class {
6780
6798
  preferredTheme: this.config.preferredTheme,
6781
6799
  successRate,
6782
6800
  lastRunDate: formatDate(/* @__PURE__ */ new Date()),
6783
- projects: this.projectSet
6801
+ projects: this.projectSet,
6802
+ allTags: Array.from(allTags),
6803
+ showProject: this.config.showProject || false,
6804
+ title: this.config.title || "Ortoni Playwright Test Report"
6784
6805
  };
6785
6806
  return template(data);
6786
6807
  } catch (error) {
@@ -6667,7 +6667,7 @@ var OrtoniReport = class {
6667
6667
  result.stdout.concat(result.stderr).map((log) => log).join("\n")
6668
6668
  ),
6669
6669
  filePath,
6670
- projects: this.projectSet,
6670
+ filters: this.projectSet,
6671
6671
  base64Image: this.config.base64Image
6672
6672
  };
6673
6673
  this.attachFiles(result, testResult);
@@ -6717,12 +6717,15 @@ var OrtoniReport = class {
6717
6717
  );
6718
6718
  const totalDuration = msToTime(result.duration);
6719
6719
  this.groupResults();
6720
+ import_handlebars.default.registerHelper("joinWithSpace", (array) => array.join(" "));
6720
6721
  import_handlebars.default.registerHelper("json", (context) => safeStringify(context));
6721
6722
  import_handlebars.default.registerHelper(
6722
6723
  "eq",
6723
6724
  (actualStatus, expectedStatus) => actualStatus === expectedStatus
6724
6725
  );
6725
- const outputFilename = ensureHtmlExtension(this.config.filename || "ortoni-report.html");
6726
+ const outputFilename = ensureHtmlExtension(
6727
+ this.config.filename || "ortoni-report.html"
6728
+ );
6726
6729
  const html = this.generateHTML(filteredResults, totalDuration);
6727
6730
  const outputPath = path2.resolve(process.cwd(), outputFilename);
6728
6731
  fs.writeFileSync(outputPath, html);
@@ -6732,14 +6735,24 @@ var OrtoniReport = class {
6732
6735
  }
6733
6736
  }
6734
6737
  groupResults() {
6735
- this.groupedResults = this.results.reduce((acc, result, index) => {
6736
- const { filePath, suite, projectName } = result;
6737
- acc[filePath] = acc[filePath] || {};
6738
- acc[filePath][suite] = acc[filePath][suite] || {};
6739
- acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
6740
- acc[filePath][suite][projectName].push({ ...result, index });
6741
- return acc;
6742
- }, {});
6738
+ if (this.config.showProject) {
6739
+ this.groupedResults = this.results.reduce((acc, result, index) => {
6740
+ const { filePath, suite, projectName } = result;
6741
+ acc[filePath] = acc[filePath] || {};
6742
+ acc[filePath][suite] = acc[filePath][suite] || {};
6743
+ acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
6744
+ acc[filePath][suite][projectName].push({ ...result, index });
6745
+ return acc;
6746
+ }, {});
6747
+ } else {
6748
+ this.groupedResults = this.results.reduce((acc, result, index) => {
6749
+ const { filePath, suite } = result;
6750
+ acc[filePath] = acc[filePath] || {};
6751
+ acc[filePath][suite] = acc[filePath][suite] || [];
6752
+ acc[filePath][suite].push({ ...result, index });
6753
+ return acc;
6754
+ }, {});
6755
+ }
6743
6756
  }
6744
6757
  generateHTML(filteredResults, totalDuration) {
6745
6758
  try {
@@ -6758,6 +6771,11 @@ var OrtoniReport = class {
6758
6771
  );
6759
6772
  const template = import_handlebars.default.compile(templateSource);
6760
6773
  const logo = this.config.logo ? path2.resolve(this.config.logo) : void 0;
6774
+ const allTags = /* @__PURE__ */ new Set();
6775
+ this.results.forEach((result) => {
6776
+ result.suiteTags.forEach((tag) => allTags.add(tag));
6777
+ result.testTags.forEach((tag) => allTags.add(tag));
6778
+ });
6761
6779
  const data = {
6762
6780
  logo,
6763
6781
  totalDuration,
@@ -6776,7 +6794,10 @@ var OrtoniReport = class {
6776
6794
  preferredTheme: this.config.preferredTheme,
6777
6795
  successRate,
6778
6796
  lastRunDate: formatDate(/* @__PURE__ */ new Date()),
6779
- projects: this.projectSet
6797
+ projects: this.projectSet,
6798
+ allTags: Array.from(allTags),
6799
+ showProject: this.config.showProject || false,
6800
+ title: this.config.title || "Ortoni Playwright Test Report"
6780
6801
  };
6781
6802
  return template(data);
6782
6803
  } catch (error) {
@@ -1,13 +1,16 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en" data-theme="{{preferredTheme}}">
3
-
4
3
  <head>
5
4
  <meta charset="UTF-8">
6
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description" content="Playwright HTML report by LetCode Koushik - V1.1.7">
8
- <title>Ortoni Playwright Test Report</title>
6
+ <meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.2">
7
+ <title>{{title}}</title>
9
8
  <link rel="icon" href="node_modules/ortoni-report/dist/icon/32.png" type="image/x-icon">
10
9
  <link rel="stylesheet" href="node_modules/ortoni-report/dist/css/main.css">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet">
11
14
  </head>
12
15
  <style>
13
16
  #summary, #testDetails, button#back-to-summary {
@@ -15,8 +18,10 @@
15
18
  }
16
19
 
17
20
  .filter.active {
18
- background-color: var(--bulma-background-active);
21
+ box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
22
+ font-weight: bold;
19
23
  }
24
+
20
25
  ::-webkit-scrollbar {
21
26
  width: 8px;
22
27
  height: 8px;
@@ -27,7 +32,7 @@
27
32
  }
28
33
 
29
34
  ::-webkit-scrollbar-thumb {
30
- background: #888;
35
+ background: var(--bulma-primary);
31
36
  border-radius: 4px;
32
37
  }
33
38
 
@@ -72,9 +77,18 @@
72
77
  .logoimage{
73
78
  max-width: 100px;
74
79
  }
80
+ .listselected {
81
+ border-image: linear-gradient(45deg, var(--bulma-primary),#FF6B6B) 1;
82
+ font-weight: bold;
83
+ }
84
+ .has-background-flaky {
85
+ background-color: #ffb70f !important;
86
+ }
87
+ .has-background-retry {
88
+ background-color: #69748c !important;
89
+ }
75
90
 
76
91
  </style>
77
-
78
92
  <body>
79
93
  <section class="section">
80
94
  <header class="container">
@@ -95,6 +109,9 @@
95
109
  <div class="control">
96
110
  <input class="input" name="search" type="search" placeholder="Search by test title" />
97
111
  </div>
112
+ <div class="control has-text-info">
113
+ <span id="selected-filters"></span>
114
+ </div>
98
115
  </div>
99
116
  <div class="column is-1">
100
117
  <div class="control">
@@ -108,22 +125,44 @@
108
125
  <main class="container">
109
126
  <div class="columns">
110
127
  <aside class="column is-two-fifths">
111
- <div class="columns is-mobile">
128
+ <div class="columns is-mobile is-vcentered">
112
129
  <div class="column">
113
130
  <h2 class="title is-4">Tests</h2>
114
131
  </div>
115
132
  <div class="column">
116
- <div class="select is-pulled-right">
117
- <select id="project-filter">
118
- <option value="all">All Projects</option>
133
+ <div id="project-filter" class="dropdown is-right is-hoverable is-pulled-right">
134
+ <div class="dropdown-trigger">
135
+ <button class="button" aria-haspopup="true" aria-controls="select-filter">
136
+ <span>Filters</span>
137
+ <span class="icon is-small">
138
+ <i class="fas fa-angle-down" aria-hidden="true"></i>
139
+ </span>
140
+ </button>
141
+ </div>
142
+ <div class="dropdown-menu" id="select-filter" role="menu">
143
+ <div class="dropdown-content">
119
144
  {{#each projects}}
120
- <option value="{{this}}">{{this}}</option>
145
+ <div class="dropdown-item">
146
+ <label class="checkbox">
147
+ <input type="checkbox" data-filter-type="project" value="{{this}}">{{this}}
148
+ </label>
149
+ </div>
121
150
  {{/each}}
122
- </select>
123
- </div>
151
+ <hr class="dropdown-divider" />
152
+ {{#each allTags}}
153
+ <div class="dropdown-item">
154
+ <label class="checkbox">
155
+ <input type="checkbox" data-filter-type="test-tags" value="{{this}}">{{this}}
156
+ </label>
157
+ </div>
158
+ {{/each}}
159
+ </div>
160
+ </div>
161
+ </div>
124
162
  </div>
125
163
  </div>
126
164
  <div class="content sidebar">
165
+ {{#if showProject}}
127
166
  {{#each groupedResults}}
128
167
  <details class="box">
129
168
  <summary class="is-size-5 has-icon-right">
@@ -134,9 +173,9 @@
134
173
  <span>{{@key}}</span>
135
174
  </div>
136
175
  </summary>
137
- <ul>
176
+ <ul class="mt-4 mb-4">
138
177
  {{#each this}}
139
- <details class="mt-1">
178
+ <details class="mt-2 mb-2">
140
179
  <summary class="is-size-5 is-capitalized">
141
180
  <div class="icon-text">
142
181
  <span class="icon has-text-info">
@@ -145,14 +184,14 @@
145
184
  <span>{{@key}}</span>
146
185
  </div>
147
186
  </summary>
148
- <ul>
187
+ <ul class="mt-4 mb-4">
149
188
  {{#each this}}
150
- <details class="mb-1">
189
+ <details class="mt-2 mb-2">
151
190
  <summary class="is-capitalized is-size-6">{{@key}}</summary>
152
- <ul>
191
+ <ul class="mt-4 mb-4">
153
192
  {{#each this}}
154
- <li class="media" data-suite-name="{{suite}}"
155
- data-test-duration="{{duration}}"
193
+ <li class="media {{#if (eq status 'skipped')}}is-hidden{{/if}}" data-suite-name="{{suite}}"
194
+ data-test-duration="{{duration}}" data-test-tags="{{joinWithSpace testTags}}"
156
195
  data-project-name="{{projectName}}" data-test-id="{{index}}"
157
196
  data-test-status="{{status}} {{retry}}">
158
197
  <div class="icon-text">
@@ -200,6 +239,77 @@
200
239
  </ul>
201
240
  </details>
202
241
  {{/each}}
242
+ {{else}}
243
+ {{#each groupedResults}}
244
+ <details class="box">
245
+ <summary class="is-size-5 has-icon-right">
246
+ <div class="icon-text">
247
+ <span class="icon has-text-info">
248
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/file.png" alt="file name">
249
+ </span>
250
+ <span>{{@key}}</span>
251
+ </div>
252
+ </summary>
253
+ <ul class="mt-4 mb-4">
254
+ {{#each this}}
255
+ <details class="mt-2 mb-2">
256
+ <summary class="is-size-5 is-capitalized">
257
+ <div class="icon-text">
258
+ <span class="icon has-text-info">
259
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
260
+ </span>
261
+ <span>{{@key}}</span>
262
+ </div>
263
+ </summary>
264
+ <ul class="mt-4 mb-4">
265
+ {{#each this}}
266
+ <li class="media {{#if (eq status 'skipped')}}is-hidden{{/if}}" data-suite-name="{{suite}}"
267
+ data-test-duration="{{duration}}" data-test-tags="{{joinWithSpace testTags}}"
268
+ data-project-name="{{projectName}}" data-test-id="{{index}}"
269
+ data-test-status="{{status}} {{retry}}">
270
+ <div class="icon-text">
271
+ {{#if isRetry}}
272
+ <span class="icon has-text-info">
273
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/retry.png"
274
+ alt="Retry"></span>
275
+ {{/if}}
276
+ {{#if (eq status "passed")}}
277
+ <span class="icon has-text-info">
278
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png"
279
+ alt="Pass"></span>
280
+ {{/if}}
281
+ {{#if (eq status "failed")}}
282
+ <span class="icon has-text-info">
283
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png"
284
+ alt="Fail"></span>
285
+ {{else}}
286
+ {{#if (eq status "skipped")}}
287
+ <span class="icon has-text-info">
288
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/skip.png"
289
+ alt="Skip"></span>
290
+ {{/if}}
291
+ {{/if}}
292
+ {{#if (eq status "timedOut")}}
293
+ <span class="icon has-text-info">
294
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/timeout.png"
295
+ alt="timedOut"></span>
296
+ {{/if}}
297
+ {{#if (eq status "flaky")}}
298
+ <span class="icon has-text-info">
299
+ <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/flaky.png"
300
+ alt="flaky"></span>
301
+ {{/if}}
302
+ <span>{{title}}</span>
303
+ </div>
304
+ </li>
305
+ {{/each}}
306
+ </ul>
307
+ </details>
308
+ {{/each}}
309
+ </ul>
310
+ </details>
311
+ {{/each}}
312
+ {{/if}}
203
313
  </div>
204
314
  </aside>
205
315
  <section class="column is-three-fifths">
@@ -207,37 +317,52 @@
207
317
  <div id="summary">
208
318
  <div class="columns is-multiline">
209
319
  <div class="column is-one-third">
210
- <div class="card is-clickable filter" data-status="all">
320
+ <div class="card is-clickable has-background-primary filter" data-status="all">
211
321
  <header class="card-header has-text-centered">
212
- <p class="card-header-title">All Tests</p>
322
+ <p class="card-header-title has-text-white">All Tests</p>
323
+ <button class="card-header-icon" aria-label="more options">
324
+ <span class="icon">
325
+ <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
326
+ </span>
327
+ </button>
213
328
  </header>
214
329
  <div class="card-content">
215
330
  <div class="content">
216
- <p class="has-text-centered has-text-primary">{{totalCount}}</p>
331
+ <p class="has-text-centered has-text-white">{{totalCount}}</p>
217
332
  </div>
218
333
  </div>
219
334
  </div>
220
335
  </div>
221
336
  <div class="column is-one-third">
222
- <div class="card is-clickable filter" data-status="passed">
337
+ <div class="card is-clickable has-background-success filter" data-status="passed">
223
338
  <header class="card-header has-text-centered">
224
- <p class="card-header-title">Passed</p>
339
+ <p class="card-header-title has-text-white">Passed</p>
340
+ <button class="card-header-icon" aria-label="more options">
341
+ <span class="icon">
342
+ <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png" alt="test name">
343
+ </span>
344
+ </button>
225
345
  </header>
226
346
  <div class="card-content">
227
347
  <div class="content">
228
- <p class="has-text-centered has-text-success">{{passCount}}</p>
348
+ <p class="has-text-centered has-text-white">{{passCount}}</p>
229
349
  </div>
230
350
  </div>
231
351
  </div>
232
352
  </div>
233
353
  <div class="column is-one-third">
234
- <div class="card is-clickable filter" data-status="failed">
354
+ <div class="card is-clickable has-background-danger filter" data-status="failed">
235
355
  <header class="card-header">
236
- <p class="card-header-title has-text-centered">Failed</p>
356
+ <p class="card-header-title has-text-white has-text-centered">Failed</p>
357
+ <button class="card-header-icon" aria-label="more options">
358
+ <span class="icon">
359
+ <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png" alt="test name">
360
+ </span>
361
+ </button>
237
362
  </header>
238
363
  <div class="card-content">
239
364
  <div class="content">
240
- <p class="has-text-centered has-text-danger">{{failCount}}</p>
365
+ <p class="has-text-centered has-text-white">{{failCount}}</p>
241
366
  </div>
242
367
  </div>
243
368
  </div>
@@ -245,37 +370,37 @@
245
370
  </div>
246
371
  <div class="columns is-multiline">
247
372
  <div class="column is-one-third">
248
- <div class="card is-clickable filter" data-status="skipped">
373
+ <div class="card is-clickable has-background-info filter" data-status="skipped">
249
374
  <header class="card-header has-text-centered">
250
- <p class="card-header-title">Skipped</p>
375
+ <p class="card-header-title has-text-white">Skipped</p>
251
376
  </header>
252
377
  <div class="card-content">
253
378
  <div class="content">
254
- <p class="has-text-centered has-text-info">{{skipCount}}</p>
379
+ <p class="has-text-centered has-text-white">{{skipCount}}</p>
255
380
  </div>
256
381
  </div>
257
382
  </div>
258
383
  </div>
259
384
  <div class="column is-one-third">
260
- <div class="card is-clickable filter" data-status="flaky">
385
+ <div class="card is-clickable has-background-warning filter" data-status="flaky">
261
386
  <header class="card-header has-text-centered">
262
- <p class="card-header-title">Flaky</p>
387
+ <p class="card-header-title has-text-white">Flaky</p>
263
388
  </header>
264
389
  <div class="card-content">
265
390
  <div class="content">
266
- <p class="has-text-centered has-text-warning">{{flakyCount}}</p>
391
+ <p class="has-text-centered has-text-white">{{flakyCount}}</p>
267
392
  </div>
268
393
  </div>
269
394
  </div>
270
395
  </div>
271
396
  <div class="column is-one-third">
272
- <div class="card is-clickable filter" data-status="retry">
397
+ <div class="card is-clickable has-background-retry filter" data-status="retry">
273
398
  <header class="card-header has-text-centered">
274
- <p class="card-header-title">Retry</p>
399
+ <p class="card-header-title has-text-white">Retry</p>
275
400
  </header>
276
401
  <div class="card-content">
277
402
  <div class="content">
278
- <p class="has-text-centered has-text-info">{{retryCount}}</p>
403
+ <p class="has-text-centered has-text-white">{{retryCount}}</p>
279
404
  </div>
280
405
  </div>
281
406
  </div>
@@ -304,7 +429,6 @@
304
429
  <div id="testDetails" style="display: none;">
305
430
  <!-- Back button should be outside the dynamic content -->
306
431
  <button class="button content" id="back-to-summary"onclick="showSummary()">Back to Summary</button>
307
- <div class="tags" id="attachTags"></div>
308
432
  <!-- Test Details will be displayed here -->
309
433
  </div>
310
434
  </section>
@@ -313,8 +437,6 @@
313
437
  </section>
314
438
  <script src="node_modules/ortoni-report/dist/utils/chart.js"></script>
315
439
  <script>
316
-
317
-
318
440
  function escapeHtml(unsafe) {
319
441
  return unsafe.replace(/[&<"']/g, function (match) {
320
442
  const escapeMap = {
@@ -327,7 +449,6 @@
327
449
  return escapeMap[match];
328
450
  });
329
451
  }
330
-
331
452
  document.addEventListener('DOMContentLoaded', () => {
332
453
  const testData = {{{ json results }}};
333
454
  const testDetails = document.getElementById('testDetails');
@@ -368,19 +489,7 @@
368
489
  testDetails.style.display = 'none';
369
490
  backButton.style.display = 'none';
370
491
  }
371
-
372
492
  window.showSummary = showSummary;
373
-
374
- function attachTags(testTags){
375
- const tags = document.querySelector("#attachTags");
376
- testTags.forEach(tag => {
377
- const tagElement = document.createElement('span');
378
- tagElement.className = 'tag is-info';
379
- tagElement.textContent = tag;
380
- tags.appendChild(tagElement);
381
- });
382
- }
383
-
384
493
  function displayTestDetails(test) {
385
494
  const summary = document.getElementById('summary');
386
495
  const testDetails = document.getElementById('testDetails');
@@ -418,7 +527,20 @@
418
527
  ${test.duration.length > 0 ? `
419
528
  <h4 class="title is-4">Duration</h4>
420
529
  <p class="${statusClass}">${test.duration}</p>` : ""}
421
- <div class="tags" id="attachTags"></div>
530
+ ${test.projectName.length > 0 ? `
531
+ <div class="control">
532
+ <div class="tags has-addons">
533
+ <span class="tag is-dark">Project</span>
534
+ <span class="tag is-info is-capitalized">${test.projectName}</span>
535
+ </div>
536
+ </div>`: ""}
537
+ ${test.testTags.length > 0 ? `
538
+ <div class="control mt-4">
539
+ <div class="tags has-addons">
540
+ <span class="tag is-dark">Test Tags</span>
541
+ <span class="tag is-info">${test.testTags.join(" ")}</span>
542
+ </div>
543
+ </div>`: ""}
422
544
  ${test.videoPath ? `
423
545
  <div id="testVideo" class="modal">
424
546
  <div class="modal-background"></div>
@@ -432,7 +554,7 @@
432
554
  </div>
433
555
  <button onclick="closeVideo()" class="modal-close is-large" aria-label="close"></button>
434
556
  </div>
435
- <button class="button" onclick="openVideo()">Attachment: Video</button>
557
+ <button class="button mt-4" onclick="openVideo()">Attachment: Video</button>
436
558
  `:''}
437
559
  </div>
438
560
  <div class="column content">
@@ -484,7 +606,6 @@
484
606
  }
485
607
  stepDetailsDiv.appendChild(stepsList);
486
608
  }
487
- attachTags(test.testTags);
488
609
  }
489
610
 
490
611
  function attachSteps(test) {
@@ -534,15 +655,20 @@
534
655
  }});
535
656
 
536
657
  function attachEventListeners() {
658
+ const checkboxes = document.querySelectorAll('#select-filter input[type="checkbox"]');
659
+ checkboxes.forEach(checkbox => {
660
+ checkbox.addEventListener('change', applyFilters);
661
+ });
537
662
  const testItems = document.querySelectorAll('[data-test-id]');
538
663
  testItems.forEach(item => {
539
664
  item.addEventListener('click', () => {
665
+ testItems.forEach(i => i.classList.remove('listselected'));
666
+ item.classList.add('listselected');
540
667
  const testId = item.getAttribute('data-test-id');
541
668
  const test = testData[testId];
542
669
  displayTestDetails(test);
543
670
  });
544
671
  });
545
-
546
672
  const filters = document.querySelectorAll('.filter');
547
673
  filters.forEach(filter => {
548
674
  filter.addEventListener('click', () => {
@@ -556,34 +682,39 @@
556
682
  applyFilters();
557
683
  });
558
684
  });
559
-
560
- const projectFilter = document.getElementById('project-filter');
561
- projectFilter.addEventListener('change', () => {
562
- applyFilters();
563
- });
564
685
  }
565
686
 
566
687
  function applyFilters() {
567
- const selectedProject = document.getElementById('project-filter').value;
688
+ const selectedCheckboxes = document.querySelectorAll('#select-filter input[type="checkbox"]:checked');
568
689
  const activeFilter = document.querySelector('.filter.active');
569
690
  const selectedStatus = activeFilter ? activeFilter.getAttribute('data-status') : 'all';
570
-
571
- const testItems = document.querySelectorAll('li[data-test-id]');
572
- const detailsElements = document.querySelectorAll('details');
573
-
691
+ const selectedProjects = [];
692
+ const selectedTags = [];
693
+ selectedCheckboxes.forEach(checkbox => {
694
+ if (checkbox.getAttribute('data-filter-type') === 'project') {
695
+ selectedProjects.push(checkbox.value.trim());
696
+ } else {
697
+ selectedTags.push(checkbox.value.trim());
698
+ }
699
+ });
700
+ const detailsElements = document.querySelectorAll('.sidebar details');
574
701
  detailsElements.forEach(details => {
575
702
  let shouldShowDetails = false;
576
703
  const items = details.querySelectorAll('li[data-test-id]');
577
704
  items.forEach(item => {
705
+ const testTags = item.getAttribute('data-test-tags').trim().split(' ').filter(Boolean);
578
706
  const projectName = item.getAttribute('data-project-name').trim();
579
707
  const testStatus = item.getAttribute('data-test-status').trim();
580
- const matchesProject = (selectedProject === 'all' || projectName === selectedProject);
581
- const matchesStatus = (selectedStatus === 'all' || testStatus.includes(selectedStatus) ||
582
- (selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
583
- (selectedStatus === 'retry' && testStatus.includes('retry')) ||
584
- (selectedStatus === 'flaky' && testStatus.includes('flaky')));
585
-
586
- if (matchesProject && matchesStatus) {
708
+ const matchesProject = selectedProjects.length === 0 || selectedProjects.includes(projectName);
709
+ const matchesTags = selectedTags.length === 0 || selectedTags.every(tag => testTags.includes(tag));
710
+ const matchesStatus = (selectedStatus === 'all' && testStatus !== 'skipped') ||
711
+ (selectedStatus !== 'all' && (
712
+ testStatus.includes(selectedStatus) ||
713
+ (selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
714
+ (selectedStatus === 'retry' && testStatus.includes('retry')) ||
715
+ (selectedStatus === 'flaky' && testStatus.includes('flaky'))
716
+ ));
717
+ if (matchesProject && matchesTags && matchesStatus) {
587
718
  item.classList.remove('is-hidden');
588
719
  shouldShowDetails = true;
589
720
  } else {
@@ -593,8 +724,27 @@
593
724
  details.open = shouldShowDetails;
594
725
  details.classList.toggle('is-hidden', !shouldShowDetails);
595
726
  });
727
+ updateSelectedFiltersDisplay(selectedProjects, selectedTags, selectedStatus);
596
728
  }
729
+
730
+ function updateSelectedFiltersDisplay(projects, tags, status) {
731
+ const filtersDisplay = document.getElementById('selected-filters');
732
+ filtersDisplay.innerHTML = '';
597
733
 
734
+ if (projects.length > 0) {
735
+ filtersDisplay.innerHTML += `<span> Projects: ${projects.join(', ')}</span>`;
736
+ }
737
+ if (tags.length > 0) {
738
+ filtersDisplay.innerHTML += `<span> Tags: ${tags.join(', ')}</span>`;
739
+ }
740
+ if (status !== 'all') {
741
+ filtersDisplay.innerHTML += `<span> Status: ${status}</span>`;
742
+ }
743
+
744
+ if (filtersDisplay.innerHTML === '') {
745
+ filtersDisplay.innerHTML = '<span>All Tests</span>';
746
+ }
747
+ }
598
748
  const searchInput = document.querySelector('input[name="search"]');
599
749
  const detailsElements = document.querySelectorAll('details');
600
750
  function filterTests(search){
@@ -652,7 +802,7 @@
652
802
  labels: ['Passed', 'Failed', 'Skipped','Flaky'],
653
803
  datasets: [{
654
804
  data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
655
- backgroundColor: ['#28a745', '#dc3545', '#d5d4a1', '#FFB704']
805
+ backgroundColor: ['#28a745', '#ff6685', '#66d1ff', '#ffb70f']
656
806
  }]
657
807
  },
658
808
  options: {
@@ -660,7 +810,7 @@
660
810
  maintainAspectRatio: false,
661
811
  plugins: {
662
812
  legend: {
663
- position: 'right'
813
+ display: false
664
814
  }
665
815
  }
666
816
  }
@@ -668,7 +818,6 @@
668
818
 
669
819
  attachEventListeners();
670
820
  });
671
-
672
821
  </script>
673
822
  </body>
674
823
  </html>
@@ -44,7 +44,6 @@ function formatDate(date) {
44
44
  return `${day}-${month}-${year} ${time}`;
45
45
  }
46
46
  exports.formatDate = formatDate;
47
- ;
48
47
  function safeStringify(obj, indent = 2) {
49
48
  const cache = new Set();
50
49
  const json = JSON.stringify(obj, (key, value) => {
@@ -68,4 +67,3 @@ function ensureHtmlExtension(filename) {
68
67
  return `${filename}.html`;
69
68
  }
70
69
  exports.ensureHtmlExtension = ensureHtmlExtension;
71
- ;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ortoni-report",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Playwright Report By LetCode with Koushik",
5
5
  "bin": {
6
6
  "ortoni-report": "./dist/cli/cli.js"
@@ -40,10 +40,10 @@
40
40
  "@playwright/test": "^1.44.1",
41
41
  "@types/node": "^22.0.2",
42
42
  "colors": "^1.4.0",
43
+ "commander": "^12.1.0",
43
44
  "handlebars": "^4.7.8",
44
45
  "tsup": "^6.5.0",
45
- "typescript": "^4.9.4",
46
- "commander": "^12.1.0"
46
+ "typescript": "^4.9.4"
47
47
  },
48
48
  "dependencies": {
49
49
  "parcel": "^2.12.0"
@@ -51,4 +51,4 @@
51
51
  "main": "dist/ortoni-report.js",
52
52
  "module": "dist/ortoni-report.mjs",
53
53
  "types": "dist/ortoni-report.d.ts"
54
- }
54
+ }
package/readme.md CHANGED
@@ -1,10 +1,14 @@
1
- # Playwright Report by Koushik
1
+ Here's an improved version of the README file that incorporates the recent changes:
2
2
 
3
- Welcome to Ortoni Report (Playwright report - unofficial), a robust HTML report generator tailored for Playwright tests. Ortoni Report introduces powerful features to enhance test reporting, making it easier to visualize and manage test results.
3
+ ---
4
+
5
+ # Ortoni Report (Unofficial Playwright Report) by Koushik
6
+
7
+ Welcome to **Ortoni Report**, a robust HTML report generator tailored for Playwright tests. Ortoni Report introduces powerful features to enhance test reporting, making it easier to visualize and manage test results.
4
8
 
5
9
  Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
6
10
 
7
- ![alt text](Ortoni-Report.png)
11
+ ![Ortoni Report Preview](Ortoni-Report.png)
8
12
 
9
13
  ## Key Features
10
14
 
@@ -15,7 +19,7 @@ Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
15
19
  - Each suite includes categorized tests under respective projects, providing clear organization.
16
20
 
17
21
  3. **Comprehensive Test Details**
18
- - Display test status (passed, failed, skipped), duration, errors, logs, and screenshots.
22
+ - Display test status (passed, failed, skipped), duration, tags, errors, logs, and screenshots.
19
23
  - Sort and filter tests by suite, project, and script for detailed insights.
20
24
 
21
25
  4. **Summary Statistics**
@@ -31,8 +35,8 @@ Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
31
35
 
32
36
  7. **Customization and Themes**
33
37
  - Customize project details, author name, test types, and toggle between dark/light themes.
34
- - Option to choose between Base64 images or file path for screenshots.
35
- - User can set the report file name
38
+ - Option to choose between Base64 images or file paths for screenshots.
39
+ - Users can set the report file name.
36
40
 
37
41
  8. **Responsive Design**
38
42
  - Optimized layout that adapts seamlessly to different screen sizes for accessibility.
@@ -40,17 +44,28 @@ Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
40
44
  9. **Integration and Configuration**
41
45
  - Easily integrate with Playwright configurations using TypeScript/JavaScript.
42
46
  - Configure reporting preferences within your Playwright setup.
43
-
44
- 10. **Add logo to the report**
45
- - Add relative or absolute path of the image to the config.
46
47
 
47
- 11. **Share report**
48
- - Generat the report and zip the folder and share.
48
+ 10. **Add Logo to the Report**
49
+ - Add a relative or absolute path of the image to the config.
50
+
51
+ 11. **Share Report**
52
+ - Generate the report, zip the folder, and share it.
53
+
54
+ 12. **Advanced Filtering**
55
+ - Filter tests by project, tags, and status simultaneously, with the ability to display only those tests matching the selected criteria.
56
+
57
+ 13. **Colorful Dashboard**
58
+ - Enhanced the visual appeal of the dashboard with vibrant and intuitive colors.
49
59
 
60
+ 14. **Display Selected Status on UI**
61
+ - The selected status filter is now visible on the UI, making it easier to track the current filter settings.
50
62
 
51
- ### How to Use ortoni-report
63
+ 15. **Hide Skipped Tests by Default**
64
+ - Skipped tests are now hidden by default when using the "All Tests" filter, providing a cleaner and more focused view of relevant tests.
52
65
 
53
- ### Installation
66
+ ### How to Use Ortoni Report
67
+
68
+ #### Installation
54
69
 
55
70
  1. **Install the ortoni-report package**:
56
71
 
@@ -58,9 +73,9 @@ Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
58
73
  npm install -g ortoni-report
59
74
  ```
60
75
 
61
- ### Generate and Bundle HTML Report
76
+ #### Generate and Bundle HTML Report
62
77
 
63
- 2. **Generate and bundle the report** (Optional):
78
+ 2. **Generate and bundle the report**:
64
79
 
65
80
  ```sh
66
81
  npx ortoni-report gr -f ortoni-report.html
@@ -69,7 +84,7 @@ Explore the live demo: [OrtoniReport Demo](https://ortoni.netlify.app/)
69
84
  ### Command Overview
70
85
 
71
86
  - `npx ortoni-report gr -f <filename>`: Bundle the specified report file.
72
- - `-f, --filename <filename>`: Specify the filename for the generated report (default: `ortoni-report.html`).
87
+ - `-f, --filename <filename>`: Specify the filename for the generated report (default: `ortoni-report.html`).
73
88
 
74
89
  ### Configurable Report Generation
75
90
 
@@ -80,13 +95,15 @@ import { defineConfig } from '@playwright/test';
80
95
  import { OrtoniReportConfig } from 'ortoni-report';
81
96
 
82
97
  const reportConfig: OrtoniReportConfig = {
83
- filename:"myawesomereport.html",
84
- logo: "path/logo.png",
98
+ base64Image: true,
99
+ logo: "logo.png",
100
+ title: "Playwight Sample Project",
101
+ showProject: true,
102
+ filename: "result",
85
103
  authorName: "LetCode Koushik",
86
- base64Image: false,
87
- preferredTheme: "dark",
88
- projectName: "Ortoni Demo Report",
89
- testType: "Smoke"
104
+ preferredTheme: "light",
105
+ projectName: "Ortoni Report Demo",
106
+ testType: "V2.0.2 - Build"
90
107
  }
91
108
 
92
109
  export default defineConfig({
@@ -96,30 +113,25 @@ export default defineConfig({
96
113
  ```
97
114
 
98
115
  ### Common Issue
116
+
99
117
  ```
100
118
  Error: Command failed:
101
119
  @parcel/namer-default: Target "main" declares an
102
120
  output file path of "index.js" which does not match
103
121
  the compiled bundle type "html".
104
122
  ```
105
- Solution:
106
- Remove "main" in the ```package.json``` file
107
123
 
108
- ### Comprehensive Test Details
124
+ **Solution**: Remove `"main"` in the `package.json` file.
109
125
 
110
- - **Rich Test Information:** Each test includes details like title, status, duration, tags, errors, steps, logs, video, and screenshot.
111
- - **Color-coded Status:** Status indicators (green for passed, red for failed, yellow for skipped) for quick identification.
112
-
113
- ### Handlebars Template Integration
126
+ ### Comprehensive Test Details
114
127
 
115
- - **Customizable Reports:** HTML reports are generated using Handlebars templates, offering flexibility in customization and styling.
116
- - **JSON Helper:** Includes a custom Handlebars helper for managing complex data structures.
128
+ - **Rich Test Information**: Each test includes details like title, status, duration, tags, errors, steps, logs, video, and screenshot.
129
+ - **Color-coded Status**: Status indicators (green for passed, red for failed, yellow for skipped) for quick identification.
117
130
 
118
131
  ### Future Plans
119
132
 
120
- - **Enhanced Customization:** Additional options for customizing report appearance and structure.
121
- - **CI/CD Integration:** Improved support for CI/CD environments.
122
- - **Advanced Filtering:** Expanded filtering capabilities to focus on specific test subsets.
133
+ - **Enhanced Customization**: Additional options for customizing report appearance and structure.
134
+ - **CI/CD Integration**: Improved support for CI/CD environments.
123
135
 
124
136
  ### Change Logs
125
137
 
@@ -133,10 +145,10 @@ View the [LICENSE](https://github.com/ortoniKC/ortoni-report/blob/main/LICENSE.m
133
145
 
134
146
  We value your feedback and contributions! For issues, suggestions, or contributions, visit our [GitHub repository](https://github.com/ortoniKC/ortoni-report).
135
147
 
136
- Thank you for choosing OrtoniReport. We're committed to enhancing your Playwright testing experience.
148
+ Thank you for choosing Ortoni Report. We're committed to enhancing your Playwright testing experience.
137
149
 
138
150
  ---
139
151
 
140
152
  **LetCode with Koushik**
141
153
 
142
- ---
154
+ ---