/** @odoo-module */ import { Component, onWillRender, useState, xml } from "@odoo/owl"; import { isFirefox } from "../../hoot-dom/hoot_dom_utils"; import { Tag } from "../core/tag"; import { Test } from "../core/test"; import { subscribeToURLParams } from "../core/url"; import { CASE_EVENT_TYPES, formatHumanReadable, formatTime, getTypeOf, isLabel, Markup, ordinal, } from "../hoot_utils"; import { HootLink } from "./hoot_link"; import { HootTechnicalValue } from "./hoot_technical_value"; /** * @typedef {import("../core/expect").CaseEvent} CaseEvent * @typedef {import("../core/expect").CaseEventType} CaseEventType * @typedef {import("../core/expect").CaseResult} CaseResult */ //----------------------------------------------------------------------------- // Global //----------------------------------------------------------------------------- const { Boolean, Map, Object: { entries: $entries, fromEntries: $fromEntries }, } = globalThis; //----------------------------------------------------------------------------- // Internal //----------------------------------------------------------------------------- /** * @param {string} label * @param {string} owner */ const stackTemplate = (label, owner) => { // Defined with string concat because line returns are taken into account in
tags. const preContent = /* xml */ `` + /* xml */ ` `; return /* xml */ `` + /* xml */ `` + /* xml */ ` `; }; const ERROR_TEMPLATE = /* xml */ ` ${label}:${preContent}${stackTemplate("Source", "event")} ${stackTemplate("Cause", "event.cause")} `; const EVENT_TEMPLATE = /* xml */ ` `; const CASE_EVENT_TYPES_INVERSE = $fromEntries( $entries(CASE_EVENT_TYPES).map(([k, v]) => [v.value, k]) ); const R_STACK_LINE_START = isFirefox() ? /^\s*(? ${stackTemplate("Source", "event")} . @)(? .*)/i : /^\s*(? at)(? .*)/i; //----------------------------------------------------------------------------- // Exports //----------------------------------------------------------------------------- /** * @typedef {{ * open?: boolean | "always"; * slots: any; * test: Test; * }} TestResultProps */ /** @extends {Component } */ export class HootTestResult extends Component { static components = { HootLink, HootTechnicalValue }; static props = { open: [{ type: Boolean }, { value: "always" }], slots: { type: Object, shape: { default: Object, }, }, test: Test, }; static template = xml` `; CASE_EVENT_TYPES = CASE_EVENT_TYPES; Tag = Tag; formatHumanReadable = formatHumanReadable; formatTime = formatTime; getTypeOf = getTypeOf; isLabel = isLabel; isMarkup = Markup.isMarkup; ordinal = ordinal; setup() { subscribeToURLParams("*"); this.config = useState(this.env.runner.config); this.logs = useState(this.props.test.logs); this.results = useState(this.props.test.results); this.state = useState({ showCode: false, showDetails: Boolean(this.props.open), }); /** @type {ReturnTyperun: } */ this.filteredEvents; onWillRender(() => { this.filteredEvents = this.getFilteredEvents(); }); } getClassName() { if (this.logs.error) { return "bg-rose-900"; } switch (this.props.test.status) { case Test.ABORTED: { return "bg-amber-900"; } case Test.FAILED: { if (this.props.test.config.todo) { return "bg-purple-900"; } else { return "bg-rose-900"; } } case Test.PASSED: { if (this.logs.warn) { return "bg-amber-900"; } else if (this.props.test.config.todo) { return "bg-purple-900"; } else { return "bg-emerald-900"; } } default: { return "bg-cyan-900"; } } } /** * @returns {[Record , Map ]} */ getFilteredEvents() { const filteredEvents = new Map(); for (const result of this.results) { filteredEvents.set(result, result.getEvents(this.config.events)); } return filteredEvents; } /** * @param {number} nType */ getTypeName(nType) { return CASE_EVENT_TYPES_INVERSE[nType]; } /** * @param {string} stack */ parseStack(stack) { const result = []; for (const line of stack.split("\n")) { const match = line.match(R_STACK_LINE_START); if (match) { result.push( { className: "text-rose", value: match.groups.prefix }, match.groups.rest + "\n" ); } else { result.push(line + "\n"); } } return result; } toggleCode() { this.state.showCode = !this.state.showCode; } toggleDetails() { if (this.props.open === "always") { return; } this.state.showDetails = !this.state.showDetails; } }