Odoo18-Base/addons/web/static/lib/hoot/ui/hoot_test_path.js
2025-01-06 10:57:38 +07:00

160 lines
5.6 KiB
JavaScript

/** @odoo-module */
import { Component, useState, xml } from "@odoo/owl";
import { Test } from "../core/test";
import { HootCopyButton } from "./hoot_copy_button";
import { HootLink } from "./hoot_link";
import { HootTagButton } from "./hoot_tag_button";
/**
* @typedef {{
* canCopy?: boolean;
* full?: boolean;
* inert?: boolean;
* showStatus?: boolean;
* test: Test;
* }} HootTestPathProps
*/
/** @extends {Component<HootTestPathProps, import("../hoot").Environment>} */
export class HootTestPath extends Component {
static components = { HootCopyButton, HootLink, HootTagButton };
static props = {
canCopy: { type: Boolean, optional: true },
full: { type: Boolean, optional: true },
inert: { type: Boolean, optional: true },
showStatus: { type: Boolean, optional: true },
test: Test,
};
static template = xml`
<t t-set="statusInfo" t-value="getStatusInfo()" />
<div class="flex items-center gap-1 whitespace-nowrap overflow-hidden">
<t t-if="props.showStatus">
<span
t-attf-class="inline-flex min-w-3 min-h-3 rounded-full bg-{{ statusInfo.className }}"
t-att-title="statusInfo.text"
/>
</t>
<span class="flex items-center overflow-hidden">
<t t-if="uiState.selectedSuiteId and !props.full">
<span class="text-muted font-bold p-1 select-none hidden md:inline">...</span>
<span class="select-none hidden md:inline">/</span>
</t>
<t t-foreach="getTestPath()" t-as="suite" t-key="suite.id">
<t t-if="props.inert">
<span
class="text-muted whitespace-nowrap font-bold p-1 hidden md:inline transition-colors"
t-esc="suite.name"
/>
</t>
<t t-else="">
<HootLink
type="'suite'"
id="suite.id"
class="'hoot-link text-muted whitespace-nowrap font-bold p-1 hidden md:inline transition-colors'"
title="'Run ' + suite.fullName"
t-esc="suite.name"
/>
<t t-if="suite.config.multi">
<strong class="text-abort whitespace-nowrap me-1">
x<t t-esc="suite.config.multi" />
</strong>
</t>
</t>
<span class="select-none hidden md:inline" t-att-class="{ 'text-skip': suite.config.skip }">/</span>
</t>
<span
class="text-primary truncate font-bold p-1"
t-att-class="{ 'text-skip': props.test.config.skip }"
t-att-title="props.test.name"
t-esc="props.test.name"
/>
<t t-if="props.canCopy">
<HootCopyButton text="props.test.name" altText="props.test.id" />
</t>
<t t-if="props.test.runCount > 1">
<strong class="text-abort whitespace-nowrap mx-1">
x<t t-esc="props.test.runCount" />
</strong>
</t>
</span>
<t t-if="props.test.tags.length">
<ul class="flex items-center gap-1">
<t t-foreach="props.test.tags.slice(0, 5)" t-as="tag" t-key="tag.name">
<li class="flex">
<HootTagButton tag="tag" />
</li>
</t>
</ul>
</t>
</div>
`;
setup() {
this.uiState = useState(this.env.ui);
}
getStatusInfo() {
switch (this.props.test.status) {
case Test.ABORTED: {
return { className: "abort", text: "aborted" };
}
case Test.FAILED: {
if (this.props.test.config.todo) {
return { className: "todo", text: "todo" };
} else {
return { className: "fail", text: "failed" };
}
}
case Test.PASSED: {
if (this.props.test.config.todo) {
return { className: "todo", text: "todo" };
} else {
return { className: "pass", text: "passed" };
}
}
default: {
return { className: "skip", text: "skipped" };
}
}
}
/**
* @param {import("../core/suite").Suite} suite
*/
getSuiteInfo(suite) {
let suites = 0;
let tests = 0;
let assertions = 0;
for (const job of suite.jobs) {
if (job instanceof Test) {
tests++;
assertions += job.lastResults?.assertions.length || 0;
} else {
suites++;
}
}
return {
id: suite.id,
name: suite.name,
parent: suite.parent?.name || null,
suites,
tests,
assertions,
};
}
getTestPath() {
const { selectedSuiteId } = this.uiState;
const { test } = this.props;
const path = test.path.slice(0, -1);
if (this.props.full || !selectedSuiteId) {
return path;
}
const index = path.findIndex((suite) => suite.id === selectedSuiteId) + 1;
return path.slice(index);
}
}