From 535acdafdce1a8b7b15c2af6c1cc33988c71a169 Mon Sep 17 00:00:00 2001 From: Esta Nagy Date: Sat, 1 Jul 2023 21:01:12 +0200 Subject: [PATCH] Add execution summary stats to the flight evaluation report (#393) - Creates new header subsection for summary view - Adds new line to header displaying total duration of the execution - Adds new tests - Removes one-liner summary from footer Resolves #388 {minor} Signed-off-by: Esta Nagy --- .../node/Gruntfile.js | 1 + .../node/css/20-colors.scss | 36 ++++ .../node/css/30-layout-common.scss | 5 - .../node/css/45-layout-summary.scss | 107 ++++++++++++ .../node/package.json | 2 +- .../flight-evaluation-report/node/report.html | 49 +++++- .../flight-evaluation-report/node/src/app.js | 12 +- .../node/src/detail-view-model.js | 156 +----------------- .../node/src/stat-model.js | 150 +++++++++++++++++ .../node/src/summary-view-model.js | 54 ++++++ .../test/data/fueltank-cucumber-test-data.js | 3 +- .../node/test/data/fueltank-test-data.js | 2 + .../node/test/data/parachute-test-data.js | 2 + .../node/test/stat-model.test.js | 37 +++++ .../node/test/summary-view-model.test.js | 18 ++ .../node/test/test-helper.js | 2 + 16 files changed, 467 insertions(+), 169 deletions(-) create mode 100644 mission-report/flight-evaluation-report/node/css/45-layout-summary.scss create mode 100644 mission-report/flight-evaluation-report/node/src/stat-model.js create mode 100644 mission-report/flight-evaluation-report/node/src/summary-view-model.js create mode 100644 mission-report/flight-evaluation-report/node/test/stat-model.test.js create mode 100644 mission-report/flight-evaluation-report/node/test/summary-view-model.test.js diff --git a/mission-report/flight-evaluation-report/node/Gruntfile.js b/mission-report/flight-evaluation-report/node/Gruntfile.js index 1945e8da..77478094 100644 --- a/mission-report/flight-evaluation-report/node/Gruntfile.js +++ b/mission-report/flight-evaluation-report/node/Gruntfile.js @@ -16,6 +16,7 @@ module.exports = function (grunt) { '../build/html-view/css/raw/30-layout-common.css': 'css/30-layout-common.scss', '../build/html-view/css/raw/35-layout-log-view.css': 'css/35-layout-log-view.scss', '../build/html-view/css/raw/40-layout-overview.css': 'css/40-layout-overview.scss', + '../build/html-view/css/raw/45-layout-summary.css': 'css/45-layout-summary.scss', '../build/html-view/css/raw/50-theme-toggle.css': 'css/50-theme-toggle.scss', '../build/html-view/css/raw/60-print.css': 'css/60-print.scss', } diff --git a/mission-report/flight-evaluation-report/node/css/20-colors.scss b/mission-report/flight-evaluation-report/node/css/20-colors.scss index 68e370de..0b50b5bf 100644 --- a/mission-report/flight-evaluation-report/node/css/20-colors.scss +++ b/mission-report/flight-evaluation-report/node/css/20-colors.scss @@ -195,6 +195,24 @@ body.light { } } } + + .summary-view { + .summary-success { + color: $light-text-color-success-prominent; + } + + .summary-failure { + color: $light-text-color-failure; + } + + .summary-abort { + color: $light-text-color-abort; + } + + .summary-suppress { + color: $light-text-color-suppressed; + } + } } body.dark { @@ -429,4 +447,22 @@ body.dark { } } } + + .summary-view { + .summary-success { + color: $dark-text-color-success-prominent; + } + + .summary-failure { + color: $dark-text-color-failure; + } + + .summary-abort { + color: $dark-text-color-abort; + } + + .summary-suppress { + color: $dark-text-color-suppressed; + } + } } diff --git a/mission-report/flight-evaluation-report/node/css/30-layout-common.scss b/mission-report/flight-evaluation-report/node/css/30-layout-common.scss index f3f8045e..fe20aaea 100644 --- a/mission-report/flight-evaluation-report/node/css/30-layout-common.scss +++ b/mission-report/flight-evaluation-report/node/css/30-layout-common.scss @@ -16,11 +16,6 @@ border: 2px solid; } -.total-stats { - padding-top: $padding-large; - padding-bottom: $padding-large; -} - .filter-module { display: block; vertical-align: top; diff --git a/mission-report/flight-evaluation-report/node/css/45-layout-summary.scss b/mission-report/flight-evaluation-report/node/css/45-layout-summary.scss new file mode 100644 index 00000000..350ece70 --- /dev/null +++ b/mission-report/flight-evaluation-report/node/css/45-layout-summary.scss @@ -0,0 +1,107 @@ +@import "01-variables"; + +body .root-container .summary-view { + position: relative; + margin-bottom: $padding-xl; + + .result-summary { + margin-left: auto; + margin-right: auto; + } + + .col-sum-time, + .col-sum-count, + .col-sum-name { + text-align: center; + } +} + +@media only screen and (max-width: $break-point-medium-screen-width-max) { + body .root-container { + $root-width: $break-point-medium-root-width; + $exec-time-width: calc($root-width / 8); + $exec-stat-width: calc($root-width / 12); + $exec-title-width: calc($root-width / 8); + + .summary-view { + + .col-sum-name, .summary-name { + width: $exec-title-width; + max-width: $exec-title-width; + padding: $padding-normal 0 $padding-normal $padding-normal; + } + + .col-sum-time, .sum-time { + width: $exec-time-width; + max-width: $exec-time-width; + padding: $padding-normal 0; + } + + .col-sum-count, .sum-stat { + width: $exec-stat-width; + max-width: $exec-stat-width; + padding: $padding-normal 0; + } + } + } +} + +@media only screen and (min-width: $break-point-large-screen-width-min) and (max-width: $break-point-large-screen-width-max) { + body .root-container { + $root-width: $break-point-large-root-width; + $exec-time-width: calc($root-width / 14); + $exec-stat-width: calc($root-width / 18); + $exec-title-width: calc($root-width / 14); + + .summary-view { + + .col-sum-name, .summary-name { + width: $exec-title-width; + max-width: $exec-title-width; + padding: $padding-normal 0 $padding-normal $padding-normal; + } + + .col-sum-time, .sum-time { + width: $exec-time-width; + max-width: $exec-time-width; + padding: $padding-normal 0; + } + + .col-sum-count, .sum-stat { + width: $exec-stat-width; + max-width: $exec-stat-width; + padding: $padding-normal 0; + } + } + } +} + +@media only screen and (min-width: $break-point-xl-screen-width-min) { + body .root-container { + $root-width: $break-point-xl-root-width; + $exec-time-width: calc($root-width / 15); + $exec-stat-width: calc($root-width / 20); + $exec-title-width: calc($root-width / 15); + + .summary-view { + + .col-sum-name, .summary-name { + width: $exec-title-width; + max-width: $exec-title-width; + padding: $padding-normal 0 $padding-normal $padding-normal; + } + + .col-sum-time, .sum-time { + width: $exec-time-width; + max-width: $exec-time-width; + padding: $padding-normal 0; + } + + .col-sum-count, .sum-stat { + width: $exec-stat-width; + max-width: $exec-stat-width; + padding: $padding-normal 0; + } + } + } +} diff --git a/mission-report/flight-evaluation-report/node/package.json b/mission-report/flight-evaluation-report/node/package.json index f4b6cfbf..71bbb6a8 100644 --- a/mission-report/flight-evaluation-report/node/package.json +++ b/mission-report/flight-evaluation-report/node/package.json @@ -30,7 +30,7 @@ } } }, - "author": "Istvan zoltan Nagy ", + "author": "Istvan Zoltan Nagy ", "license": "MIT", "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/mission-report/flight-evaluation-report/node/report.html b/mission-report/flight-evaluation-report/node/report.html index 991576e2..df936d2a 100644 --- a/mission-report/flight-evaluation-report/node/report.html +++ b/mission-report/flight-evaluation-report/node/report.html @@ -31,7 +31,48 @@

Loading telemetry...

Countdown started:

-

Mission concluded:

+

Mission concluded:

+

Total execution time:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CountExec. timeMinimumAverageMaximum
+
+
diff --git a/mission-report/flight-evaluation-report/node/src/app.js b/mission-report/flight-evaluation-report/node/src/app.js index c8182584..2dead7e1 100644 --- a/mission-report/flight-evaluation-report/node/src/app.js +++ b/mission-report/flight-evaluation-report/node/src/app.js @@ -6,6 +6,8 @@ const {TestRunModel} = require('../src/test-run-model'); const {LogViewTimelineModel} = require('../src/log-view-timeline-model'); const {TestRunFilter} = require('../src/filter'); const {DetailViewModel} = require('../src/detail-view-model'); +const {formatDuration} = require('../src/stat-model'); +const {SummaryViewModel} = require('../src/summary-view-model'); class FlightEvaluationReportInitializer { @@ -61,8 +63,7 @@ class FlightEvaluationReportInitializer { addRun(run) { this.rootModel.runs.push(run); - const result = run.result.toLowerCase(); - this.rootModel.resultCounts[result] = (this.rootModel.resultCounts[result] || 0) + 1; + this.rootModel.summaryView.addRun(run); } initStaticData() { @@ -129,7 +130,7 @@ class FlightEvaluationReportModel { this.results = []; this.allRules = []; this.runs = []; - this.resultCounts = {success: 0, failure: 0, abort: 0, suppressed: 0}; + this.summaryView = new SummaryViewModel(this); this.logViewTimeline = new LogViewTimelineModel(this); this.detailView = new DetailViewModel(this); this.filter = new TestRunFilter(this); @@ -192,6 +193,11 @@ class FlightEvaluationReportModel { return this.runs.length > 0 ? this.runs[this.runs.length - 1].end : null; } + getTotalRunTime() { + const duration = this.runs.length > 0 ? this.getLastEndDate() - this.getFirstStartDate() : null; + return formatDuration(duration, 2); + } + testStart = ko.pureComputed(function () { return this.convertToDateTimeString(this.getFirstStartDate()); }, this); diff --git a/mission-report/flight-evaluation-report/node/src/detail-view-model.js b/mission-report/flight-evaluation-report/node/src/detail-view-model.js index 5d0b8a1d..ad0ddeb2 100644 --- a/mission-report/flight-evaluation-report/node/src/detail-view-model.js +++ b/mission-report/flight-evaluation-report/node/src/detail-view-model.js @@ -1,155 +1,7 @@ // noinspection TypeScriptUMDGlobal const ko = require('knockout'); -const {KeyValueEntityModel} = require('../src/key-value-entity-model'); -const {TestRunModel} = require('../src/test-run-model'); -const {LogViewTimelineModel} = require('../src/log-view-timeline-model'); -const {TestRunFilter} = require('../src/filter'); - -class DetailViewStatModel { - constructor(rootModel, filter) { - this.rootModel = rootModel; - this.filter = filter; - this.runs = 0; - this.start = null; - this.end = null; - this.min = null; - this.max = null; - this.sum = 0; - this.success = 0; - this.failure = 0; - this.aborted = 0; - this.suppressed = 0; - this.matcherKeys = []; - this.matchers = []; - } - - merge(other) { - if (other.runs <= 0) { - return; - } - this.runs += other.runs; - this.start = this.start === null ? other.start : Math.min(other.start, this.start); - this.end = this.end === null ? other.end : Math.max(other.end, this.end); - this.min = this.min === null ? other.min : Math.min(this.min, other.min); - this.max = this.max === null ? other.max : Math.max(this.max, other.max); - this.sum += other.sum; - this.success += other.success; - this.failure += other.failure; - this.aborted += other.aborted; - this.suppressed += other.suppressed; - } - - startTimeAsString() { - return this.start === null ? 'N/A' : this.rootModel.filter.formatDateAsString(new Date(this.start)); - } - - canFilterStartTime() { - return this.start !== null && this.rootModel.filter.startTime().getTime() !== this.start; - } - - filterStartTime() { - return this.rootModel.filter.setStartTime(new Date(this.start)); - } - - endTimeAsString() { - return this.end === null ? 'N/A' : this.rootModel.filter.formatDateAsString(new Date(this.end)); - } - - canFilterEndTime() { - return this.end !== null && this.rootModel.filter.endTime().getTime() !== this.end; - } - - filterEndTime() { - return this.rootModel.filter.setEndTime(new Date(this.end)); - } - - formatDuration(duration) { - let result = ''; - if (duration === null) { - result = "N/A"; - } else { - const hasMinutes = duration >= 60000; - if (hasMinutes) { - result = (duration / 60000).toFixed(0) + "m"; - duration = duration % 60000; - } - const hasSeconds = duration >= 1000 || hasMinutes; - if (hasSeconds) { - const fractionalDigits = hasMinutes ? 0 : 1; - result += (duration / 1000).toFixed(fractionalDigits) + "s"; - } else { - result += duration.toFixed(0) + "ms"; - } - } - return result; - } - - average() { - return this.formatDuration(this.runs === 0 ? null : this.sum / this.runs); - } - - minimum() { - return this.formatDuration(this.min, 0); - } - - maximum() { - return this.formatDuration(this.max, 0); - } - - totalSum() { - return this.formatDuration(this.sum, 0); - } - - visible() { - return this.runs > 0; - } - - worstResult() { - let result; - if (this.failure > 0) { - result = "failure"; - } else if (this.aborted > 0) { - result = "aborted"; - } else if (this.suppressed > 0) { - result = "suppressed"; - } else if (this.success > 0) { - result = "success"; - } else { - result = "empty"; - } - return result; - } - - addRun(run) { - if (this.filter(run)) { - return; - } - this.runs += 1; - this.start = this.start === null ? run.start.getTime() : Math.min(this.start, run.start.getTime()); - this.end = this.end === null ? run.end.getTime() : Math.max(this.end, run.end.getTime()); - const duration = run.end - run.start; - this.min = this.min === null ? duration : Math.min(this.min, duration); - this.max = this.max === null ? duration : Math.max(this.max, duration); - this.sum += duration; - const matcherEntries = this.rootModel.matchers; - for (const item of run.matcherKeys) { - if (!this.matcherKeys.includes(item)) { - this.matcherKeys.push(item); - this.matchers.push(matcherEntries.find(m => m.key === item)); - } - } - if (run.isSuccess()) { - this.success++; - } else if (run.isFailure()) { - this.failure++; - } else if (run.isAborted()) { - this.aborted++; - } else if (run.isSuppressed()) { - this.suppressed++; - } - } -} +const {StatModel} = require('../src/stat-model'); class DetailViewMethodModel { constructor(run, methodName) { @@ -159,8 +11,8 @@ class DetailViewMethodModel { this.methodKey = run.methodKey; this.countdown = run.countdown; this.collapsed = ko.observable(this.parent.isCollapsed(run.classKey, run.methodKey)); - this.filtered = new DetailViewStatModel(run.rootModel, r => r.rows[0].visible() === false); - this.total = new DetailViewStatModel(run.rootModel, () => false); + this.filtered = new StatModel(run.rootModel, r => r.rows[0].visible() === false); + this.total = new StatModel(run.rootModel, () => false); this.addRun(run); } @@ -280,7 +132,7 @@ class DetailViewModel { }, this); summary = ko.pureComputed(function () { - const result = new DetailViewStatModel(this.rootModel, () => false); + const result = new StatModel(this.rootModel, () => false); for (const method of this.methods()) { const selectedData = method.selectedData(); result.merge(selectedData); diff --git a/mission-report/flight-evaluation-report/node/src/stat-model.js b/mission-report/flight-evaluation-report/node/src/stat-model.js new file mode 100644 index 00000000..b7493cc6 --- /dev/null +++ b/mission-report/flight-evaluation-report/node/src/stat-model.js @@ -0,0 +1,150 @@ +// noinspection TypeScriptUMDGlobal + +const ko = require('knockout'); + +const formatDuration = function(duration, secondPrecision) { + let result = ''; + if (duration === null) { + result = "N/A"; + } else { + const hasMinutes = duration >= 60000; + if (hasMinutes) { + result = (duration / 60000).toFixed(0) + "m"; + duration = duration % 60000; + } + const hasSeconds = duration >= 1000 || hasMinutes; + if (hasSeconds) { + const fractionalDigits = secondPrecision || (hasMinutes ? 0 : 1); + result += (duration / 1000).toFixed(fractionalDigits) + "s"; + } else { + result += duration.toFixed(0) + "ms"; + } + } + return result; +} +class StatModel { + constructor(rootModel, filter) { + this.rootModel = rootModel; + this.filter = filter; + this.runs = 0; + this.start = null; + this.end = null; + this.min = null; + this.max = null; + this.sum = 0; + this.success = 0; + this.failure = 0; + this.aborted = 0; + this.suppressed = 0; + this.matcherKeys = []; + this.matchers = []; + } + + merge(other) { + if (other.runs <= 0) { + return; + } + this.runs += other.runs; + this.start = this.start === null ? other.start : Math.min(other.start, this.start); + this.end = this.end === null ? other.end : Math.max(other.end, this.end); + this.min = this.min === null ? other.min : Math.min(this.min, other.min); + this.max = this.max === null ? other.max : Math.max(this.max, other.max); + this.sum += other.sum; + this.success += other.success; + this.failure += other.failure; + this.aborted += other.aborted; + this.suppressed += other.suppressed; + } + + startTimeAsString() { + return this.start === null ? 'N/A' : this.rootModel.filter.formatDateAsString(new Date(this.start)); + } + + canFilterStartTime() { + return this.start !== null && this.rootModel.filter.startTime().getTime() !== this.start; + } + + filterStartTime() { + return this.rootModel.filter.setStartTime(new Date(this.start)); + } + + endTimeAsString() { + return this.end === null ? 'N/A' : this.rootModel.filter.formatDateAsString(new Date(this.end)); + } + + canFilterEndTime() { + return this.end !== null && this.rootModel.filter.endTime().getTime() !== this.end; + } + + filterEndTime() { + return this.rootModel.filter.setEndTime(new Date(this.end)); + } + + average() { + return formatDuration(this.runs === 0 ? null : this.sum / this.runs); + } + + minimum() { + return formatDuration(this.min, 0); + } + + maximum() { + return formatDuration(this.max, 0); + } + + totalSum() { + return formatDuration(this.sum, 0); + } + + visible() { + return this.runs > 0; + } + + worstResult() { + let result; + if (this.failure > 0) { + result = "failure"; + } else if (this.aborted > 0) { + result = "aborted"; + } else if (this.suppressed > 0) { + result = "suppressed"; + } else if (this.success > 0) { + result = "success"; + } else { + result = "empty"; + } + return result; + } + + addRun(run) { + if (this.filter(run)) { + return; + } + this.runs += 1; + this.start = this.start === null ? run.start.getTime() : Math.min(this.start, run.start.getTime()); + this.end = this.end === null ? run.end.getTime() : Math.max(this.end, run.end.getTime()); + const duration = run.end - run.start; + this.min = this.min === null ? duration : Math.min(this.min, duration); + this.max = this.max === null ? duration : Math.max(this.max, duration); + this.sum += duration; + const matcherEntries = this.rootModel.matchers; + for (const item of run.matcherKeys) { + if (!this.matcherKeys.includes(item)) { + this.matcherKeys.push(item); + this.matchers.push(matcherEntries.find(m => m.key === item)); + } + } + if (run.isSuccess()) { + this.success++; + } else if (run.isFailure()) { + this.failure++; + } else if (run.isAborted()) { + this.aborted++; + } else if (run.isSuppressed()) { + this.suppressed++; + } + } +} + +module.exports.formatDuration = formatDuration; +module.exports.StatModel = StatModel; diff --git a/mission-report/flight-evaluation-report/node/src/summary-view-model.js b/mission-report/flight-evaluation-report/node/src/summary-view-model.js new file mode 100644 index 00000000..09845ad0 --- /dev/null +++ b/mission-report/flight-evaluation-report/node/src/summary-view-model.js @@ -0,0 +1,54 @@ +// noinspection TypeScriptUMDGlobal + +const ko = require('knockout'); +const {StatModel} = require('../src/stat-model'); + +const capitalize = function (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); +} + +class SummaryViewOutcomeModel { + constructor(rootModel, key) { + this.rootModel = rootModel; + this.name = capitalize(key); + this.key = key.toUpperCase(); + this.total = new StatModel(rootModel, () => false); + } + + addRun(run) { + this.total.addRun(run); + } + + resultEntity() { + const self = this; + return self.rootModel.results.find(item => { + return item.key === self.key; + }); + } + +} + +class SummaryViewModel { + + constructor(rootModel) { + this.rootModel = rootModel; + this.results = [ + new SummaryViewOutcomeModel(this.rootModel, 'success'), + new SummaryViewOutcomeModel(this.rootModel, 'failure'), + new SummaryViewOutcomeModel(this.rootModel, 'abort'), + new SummaryViewOutcomeModel(this.rootModel, 'suppressed') + ]; + this.summary = new StatModel(this.rootModel, () => false); + } + + addRun(run) { + const result = this.results.filter(item => { + return item.key === run.result.toUpperCase(); + }); + result[0].addRun(run); + this.summary.addRun(run); + } + +} + +module.exports.SummaryViewModel = SummaryViewModel; diff --git a/mission-report/flight-evaluation-report/node/test/data/fueltank-cucumber-test-data.js b/mission-report/flight-evaluation-report/node/test/data/fueltank-cucumber-test-data.js index e6dc3a79..74168dd5 100644 --- a/mission-report/flight-evaluation-report/node/test/data/fueltank-cucumber-test-data.js +++ b/mission-report/flight-evaluation-report/node/test/data/fueltank-cucumber-test-data.js @@ -271,7 +271,7 @@ const input = { } ] } - +const totalRunTime = "187ms" module.exports.fuelTankCucumber = { name: "FuelTankTest (Cucumber)", @@ -280,6 +280,7 @@ module.exports.fuelTankCucumber = { matcherNames: matcherNames, threadNames: threadNames, timestamps: times, + totalRunTime: totalRunTime, input: input, filters: filters, timeFilterScenarios: timeFilterScenarios, diff --git a/mission-report/flight-evaluation-report/node/test/data/fueltank-test-data.js b/mission-report/flight-evaluation-report/node/test/data/fueltank-test-data.js index 1d6b2b6f..218ad59b 100644 --- a/mission-report/flight-evaluation-report/node/test/data/fueltank-test-data.js +++ b/mission-report/flight-evaluation-report/node/test/data/fueltank-test-data.js @@ -625,6 +625,7 @@ const input = { } ] } +const totalRunTime = "234ms" module.exports.fuelTank = { name: "FuelTankTest", @@ -633,6 +634,7 @@ module.exports.fuelTank = { matcherNames: matcherNames, threadNames: threadNames, timestamps: times, + totalRunTime: totalRunTime, input: input, filters: filters, timeFilterScenarios: timeFilterScenarios, diff --git a/mission-report/flight-evaluation-report/node/test/data/parachute-test-data.js b/mission-report/flight-evaluation-report/node/test/data/parachute-test-data.js index 091d29e9..1b316fa8 100644 --- a/mission-report/flight-evaluation-report/node/test/data/parachute-test-data.js +++ b/mission-report/flight-evaluation-report/node/test/data/parachute-test-data.js @@ -684,6 +684,7 @@ const input = { } ] } +const totalRunTime = "73ms" module.exports.parachute = { name: "ParachuteTest", @@ -692,6 +693,7 @@ module.exports.parachute = { matcherNames: matcherNames, threadNames: threadNames, timestamps: times, + totalRunTime: totalRunTime, input: input, filters: filters, timeFilterScenarios: timeFilterScenarios, diff --git a/mission-report/flight-evaluation-report/node/test/stat-model.test.js b/mission-report/flight-evaluation-report/node/test/stat-model.test.js new file mode 100644 index 00000000..699658bf --- /dev/null +++ b/mission-report/flight-evaluation-report/node/test/stat-model.test.js @@ -0,0 +1,37 @@ +const {expect, describe, it} = require('@jest/globals'); +const {formatDuration} = require('../src/stat-model'); + +describe("formatDuration()", function () { + it("should return milliseconds when called with less than a second.", function () { + //given + const input = 987; + //when + const actual = formatDuration(input); + //then + expect(actual).toEqual("987ms"); + }); + it("should return decimals with seconds when called with seconds.", function () { + //given + const input = 1487.6; + //when + const actual = formatDuration(input); + //then + expect(actual).toEqual("1.5s"); + }); + it("should return no decimals of seconds when called with minutes.", function () { + //given + const input = 61487.6; + //when + const actual = formatDuration(input); + //then + expect(actual).toEqual("1m1s"); + }); + it("should return decimals of seconds when called with minutes and explicit decimal parameter.", function () { + //given + const input = 61487.6; + //when + const actual = formatDuration(input, 1); + //then + expect(actual).toEqual("1m1.5s"); + }); +}); diff --git a/mission-report/flight-evaluation-report/node/test/summary-view-model.test.js b/mission-report/flight-evaluation-report/node/test/summary-view-model.test.js new file mode 100644 index 00000000..4048daff --- /dev/null +++ b/mission-report/flight-evaluation-report/node/test/summary-view-model.test.js @@ -0,0 +1,18 @@ +const {expect, describe, it} = require('@jest/globals'); +const {fuelTank} = require('./data/fueltank-test-data'); +const help = require('./test-helper'); + +describe("SummaryViewOutcomeModel.resultEntity() using fuelTank class data", function () { + const testData = fuelTank; + + it("should return matching result entity.", function () { + //given + const root = help.createFlightEvaluationReportWithData(testData); + const underTest = root.summaryView.results[0]; + //when + const actual = underTest.resultEntity(); + //then + expect(actual.key).toEqual("SUCCESS"); + expect(actual.value).toEqual("Success"); + }); +}); diff --git a/mission-report/flight-evaluation-report/node/test/test-helper.js b/mission-report/flight-evaluation-report/node/test/test-helper.js index 55b83a02..8f037f43 100644 --- a/mission-report/flight-evaluation-report/node/test/test-helper.js +++ b/mission-report/flight-evaluation-report/node/test/test-helper.js @@ -41,6 +41,8 @@ function expectStartAndEndDateMatches(actualModel, inputData) { expect(actualModel.testEnd()) .toEqual(new Date(inputData.timestamps[inputData.timestamps.length - 1]) .toISOString().replaceAll("T", " ").replaceAll("Z", "")); + expect(actualModel.getTotalRunTime()) + .toEqual(inputData.totalRunTime) }