From ad12e6fdba4048a0d64f36f6d12c229316885d4a Mon Sep 17 00:00:00 2001 From: Todti Date: Mon, 6 Jan 2025 22:27:48 +0100 Subject: [PATCH 1/3] fix: a lot of small fixes and improvements --- .../assets/svg/line-alerts-alert-circle.svg | 12 ++++++ .../src/assets/svg/line-general-eye.svg | 7 ++++ .../src/assets/svg/line-icon-bomb-2.svg | 12 ++++++ .../components/app/ArrowButton/styles.scss | 2 + .../app/ReportMetadata/MetadataSummary.tsx | 40 ++++++++++--------- .../app/ReportMetadata/MetadataWithIcon.tsx | 4 +- .../app/TestResult/TestResultError/index.tsx | 29 +++++++------- .../TestResult/TestResultError/styles.scss | 21 +++++++--- .../app/TestResult/TestResultHeader/index.tsx | 4 +- .../TestResultHistoryItem.tsx | 15 +++---- .../TestResultInfo/TestResultInfoStatuses.tsx | 31 ++++++++++++++ .../app/TestResult/TestResultInfo/index.tsx | 10 +++-- .../app/TestResult/TestResultInfo/styles.scss | 17 ++++++++ .../TestResult/TestResultNavigation/index.tsx | 3 +- .../TestResultPrevStatuses/index.tsx | 12 +++--- .../TestResult/TestResultTeardown/index.tsx | 4 +- .../src/components/app/TestResult/index.tsx | 4 +- .../src/components/app/Tree/TreeItem.tsx | 4 +- .../src/components/app/Tree/styles.scss | 8 +++- .../src/components/commons/Button/styles.scss | 10 +++-- packages/web-awesome/src/i18n/constants.ts | 4 +- packages/web-awesome/src/i18n/locales/am.json | 3 +- packages/web-awesome/src/i18n/locales/az.json | 3 +- packages/web-awesome/src/i18n/locales/de.json | 3 +- packages/web-awesome/src/i18n/locales/en.json | 3 +- packages/web-awesome/src/i18n/locales/es.json | 3 +- packages/web-awesome/src/i18n/locales/fr.json | 3 +- packages/web-awesome/src/i18n/locales/he.json | 3 +- packages/web-awesome/src/i18n/locales/it.json | 3 +- packages/web-awesome/src/i18n/locales/ja.json | 3 +- packages/web-awesome/src/i18n/locales/ka.json | 3 +- packages/web-awesome/src/i18n/locales/kr.json | 3 +- packages/web-awesome/src/i18n/locales/nl.json | 3 +- packages/web-awesome/src/i18n/locales/pl.json | 3 +- packages/web-awesome/src/i18n/locales/pt.json | 3 +- packages/web-awesome/src/i18n/locales/ru.json | 3 +- packages/web-awesome/src/i18n/locales/sv.json | 3 +- packages/web-awesome/src/i18n/locales/tr.json | 3 +- packages/web-awesome/src/i18n/locales/zh.json | 3 +- packages/web-awesome/src/index.tsx | 4 ++ packages/web-awesome/src/stores/stats.ts | 2 +- .../web-awesome/src/utils/copyToClipboard.ts | 15 ++++++- 42 files changed, 235 insertions(+), 93 deletions(-) create mode 100644 packages/web-awesome/src/assets/svg/line-alerts-alert-circle.svg create mode 100644 packages/web-awesome/src/assets/svg/line-general-eye.svg create mode 100644 packages/web-awesome/src/assets/svg/line-icon-bomb-2.svg create mode 100644 packages/web-awesome/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx diff --git a/packages/web-awesome/src/assets/svg/line-alerts-alert-circle.svg b/packages/web-awesome/src/assets/svg/line-alerts-alert-circle.svg new file mode 100644 index 0000000..2d49f6c --- /dev/null +++ b/packages/web-awesome/src/assets/svg/line-alerts-alert-circle.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/packages/web-awesome/src/assets/svg/line-general-eye.svg b/packages/web-awesome/src/assets/svg/line-general-eye.svg new file mode 100644 index 0000000..4f3aa41 --- /dev/null +++ b/packages/web-awesome/src/assets/svg/line-general-eye.svg @@ -0,0 +1,7 @@ + + + + diff --git a/packages/web-awesome/src/assets/svg/line-icon-bomb-2.svg b/packages/web-awesome/src/assets/svg/line-icon-bomb-2.svg new file mode 100644 index 0000000..9420457 --- /dev/null +++ b/packages/web-awesome/src/assets/svg/line-icon-bomb-2.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/packages/web-awesome/src/components/app/ArrowButton/styles.scss b/packages/web-awesome/src/components/app/ArrowButton/styles.scss index a71f085..36d1c5a 100644 --- a/packages/web-awesome/src/components/app/ArrowButton/styles.scss +++ b/packages/web-awesome/src/components/app/ArrowButton/styles.scss @@ -2,10 +2,12 @@ background: transparent; border: none; padding: 8px 4px; + border-radius: 4px; color: var(--on-icon-secondary); &:hover { background: var(--bg-control-flat-medium); + color: var(--on-icon-primary); } } diff --git a/packages/web-awesome/src/components/app/ReportMetadata/MetadataSummary.tsx b/packages/web-awesome/src/components/app/ReportMetadata/MetadataSummary.tsx index 7330871..d8a0e94 100644 --- a/packages/web-awesome/src/components/app/ReportMetadata/MetadataSummary.tsx +++ b/packages/web-awesome/src/components/app/ReportMetadata/MetadataSummary.tsx @@ -1,7 +1,9 @@ +import type { Statistic } from "@allurereport/core-api"; import { statusesList } from "@allurereport/core-api"; import { computed } from "@preact/signals"; -import { FunctionComponent } from "preact"; -import MetadataItem, { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem"; +import type { FunctionComponent } from "preact"; +import type { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem"; +import MetadataItem from "@/components/app/ReportMetadata/MetadataItem"; import { MetadataTestType } from "@/components/app/ReportMetadata/MetadataTestType"; import { MetadataWithIcon } from "@/components/app/ReportMetadata/MetadataWithIcon"; import * as styles from "@/components/app/ReportMetadata/styles.scss"; @@ -12,6 +14,7 @@ import { capitalize } from "@/utils/capitalize"; export const MetadataSummary: FunctionComponent = () => { const { t } = useI18n("statuses"); + const { t: testSummary } = useI18n("testSummary"); return ( { type: "all", count: stats.total, })); - // TODO: https://github.com/qameta/allure3/issues/178 - // const metadataStatsKeys: (keyof Statistic)[] = ["flakyTests", "retryTests", "newTests"]; - // const metaDataTests = metadataStatsKeys - // .filter((key) => stats[key]) - // .map((key) => { - // const title = t[key]; - // const props = { title, count: stats[key], type: key }; - // - // return ( - // <> - // - // - // ); - // }); + const metaDataTests = ["flaky", "retry"] + .map((key) => { + if (!stats[key]) { + return; + } + const title = testSummary(key); + const props = { title, count: stats[key] || 0, type: key }; + + return ( +
+ +
+ ); + }) + .filter(Boolean); const metadataStatuses = statusesList .map((status) => ({ status, value: stats[status] })) @@ -67,8 +71,8 @@ export const MetadataSummary: FunctionComponent = () => { props={allTest.value} renderComponent={MetadataWithIcon} /> - {/*
*/} - {/*{metaDataTests}*/} + {Boolean(metaDataTests.length) &&
} + {metaDataTests}
{metadataStatuses}
diff --git a/packages/web-awesome/src/components/app/ReportMetadata/MetadataWithIcon.tsx b/packages/web-awesome/src/components/app/ReportMetadata/MetadataWithIcon.tsx index 25df241..0280b8f 100644 --- a/packages/web-awesome/src/components/app/ReportMetadata/MetadataWithIcon.tsx +++ b/packages/web-awesome/src/components/app/ReportMetadata/MetadataWithIcon.tsx @@ -1,8 +1,8 @@ -import { FunctionComponent, h } from "preact"; +import type { FunctionComponent } from "preact"; import notifications from "@/assets/svg/line-alerts-notification-box.svg"; import refresh from "@/assets/svg/line-arrows-refresh-ccw-1.svg"; import lineGeneralZap from "@/assets/svg/line-general-zap.svg"; -import { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem"; +import type { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem"; import { SvgIcon } from "@/components/commons/SvgIcon"; import { Text } from "@/components/commons/Typography"; import * as styles from "./styles.scss"; diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx index 673e86d..6e6d910 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx @@ -1,6 +1,7 @@ import { useState } from "preact/hooks"; import LineGeneralCopy3 from "@/assets/svg/line-general-copy-3.svg"; import { IconButton } from "@/components/commons/Button"; +import { TooltipWrapper } from "@/components/commons/Tooltip"; import { Code, Text } from "@/components/commons/Typography"; import { useI18n } from "@/stores/locale"; import { copyToClipboard } from "@/utils/copyToClipboard"; @@ -19,28 +20,26 @@ const TestResultErrorTrace = ({ trace }) => { export const TestResultError = ({ message, trace }) => { const [isOpen, setIsOpen] = useState(false); const { t } = useI18n("ui"); + const { t: tooltip } = useI18n("controls"); return (
-
setIsOpen(!isOpen)} - > +
{t("error")} - { - e.stopPropagation(); - copyToClipboard(message); - }} - /> + + { + copyToClipboard(message); + }} + /> +
-
setIsOpen(!isOpen)}> +
setIsOpen(!isOpen)}> {message} diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultError/styles.scss b/packages/web-awesome/src/components/app/TestResult/TestResultError/styles.scss index d0ba32f..cc9c3da 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultError/styles.scss +++ b/packages/web-awesome/src/components/app/TestResult/TestResultError/styles.scss @@ -1,10 +1,9 @@ .test-result-error { - padding: 8px 8px 12px 24px; + padding: 8px 8px 12px 16px; background-color: var(--bg-alpha-capella); border-radius: 8px; position: relative; overflow: hidden; - cursor: pointer; &:before { content: ""; @@ -26,15 +25,27 @@ .test-result-error-text { margin-bottom: 8px; + padding-left: 8px; color: var(--on-support-capella); } .test-result-error-trace { margin-top: 8px; + padding-left: 8px; pre { - overflow-wrap: break-word; - word-wrap: break-word; - white-space: pre-wrap; + overflow: scroll; + padding-bottom: 24px; + } +} + +.test-result-error-message { + padding: 8px; + border-radius: 8px; + cursor: pointer; + transition: background-color 300ms; + + &:hover { + background: var(--bg-alpha-capella); } } diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultHeader/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultHeader/index.tsx index 6aa718c..5db5ba2 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultHeader/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultHeader/index.tsx @@ -1,6 +1,6 @@ import clsx from "clsx"; -import { FunctionalComponent } from "preact"; -import { AllureAwesomeTestResult } from "types"; +import type { FunctionalComponent } from "preact"; +import type { AllureAwesomeTestResult } from "types"; import LineArrowsChevronDown from "@/assets/svg/line-arrows-chevron-down.svg"; import LineGeneralHomeLine from "@/assets/svg/line-general-home-line.svg"; import { LanguagePicker } from "@/components/app/LanguagePicker"; diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx index ac9253a..146ac96 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx @@ -9,16 +9,18 @@ import TreeItemIcon from "@/components/app/Tree/TreeItemIcon"; import { IconButton } from "@/components/commons/Button"; import { TooltipWrapper } from "@/components/commons/Tooltip"; import { Text } from "@/components/commons/Typography"; -import { navigateTo } from "@/index"; +import { openInNewTab } from "@/index"; +import { useI18n } from "@/stores"; import { timestampToDate } from "@/utils/time"; export const TestResultHistoryItem = ({ testResultItem }) => { - const { status, message, trace, stop, duration, id, uuid } = testResultItem; + const { status, message, trace, stop, duration, id } = testResultItem; const [isOpened, setIsOpen] = useState(false); const convertedStop = timestampToDate(stop); - const formattedDuration = formatDuration(duration); + const formattedDuration = formatDuration(duration as number); + const { t } = useI18n("controls"); - const navigateUrl = `/${uuid}/${id}`; + const navigateUrl = `/testresult/${id}`; return (
@@ -31,8 +33,7 @@ export const TestResultHistoryItem = ({ testResultItem }) => { {formattedDuration} - - + { className={styles["test-result-history-item-link"]} onClick={(e) => { e.stopPropagation(); - navigateTo(navigateUrl); + openInNewTab(navigateUrl); }} /> diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx new file mode 100644 index 0000000..742a081 --- /dev/null +++ b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx @@ -0,0 +1,31 @@ +import lineAlertsAlertCircle from "@/assets/svg/line-alerts-alert-circle.svg"; +import lineGeneralEye from "@/assets/svg/line-general-eye.svg"; +import lineIconBomb2 from "@/assets/svg/line-icon-bomb-2.svg"; +import { SvgIcon } from "@/components/commons/SvgIcon"; +import { useI18n } from "@/stores"; +import { capitalize } from "@/utils/capitalize"; +import * as styles from "./styles.scss"; + +const icons = { + flaky: lineIconBomb2.id, + known: lineAlertsAlertCircle.id, + muted: lineGeneralEye.id, +}; + +export const TestResultInfoStatuses = ({ statuses }) => { + const { t } = useI18n("filters"); + return ( +
+ {statuses.map(([status], key: number) => { + const title = t(status); + + return ( +
+ + {capitalize(title)} +
+ ); + })} +
+ ); +}; diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultInfo/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/index.tsx index c2e25a0..6cc0599 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultInfo/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/index.tsx @@ -1,7 +1,7 @@ import { formatDuration } from "@allurereport/core-api"; -import * as test from "node:test"; -import { FunctionalComponent } from "preact"; -import { AllureAwesomeTestResult } from "types"; +import type { FunctionalComponent } from "preact"; +import type { AllureAwesomeTestResult } from "types"; +import { TestResultInfoStatuses } from "@/components/app/TestResult/TestResultInfo/TestResultInfoStatuses"; import { TestResultNavigation } from "@/components/app/TestResult/TestResultNavigation"; import { TestResultPrevStatuses } from "@/components/app/TestResult/TestResultPrevStatuses"; import { TestResultSeverity } from "@/components/app/TestResult/TestResultSeverity"; @@ -19,11 +19,12 @@ export type TestResultInfoProps = { }; export const TestResultInfo: FunctionalComponent = ({ testResult }) => { - const { name, status, duration, labels, history, retries, attachments, stop } = testResult ?? {}; + const { name, status, muted, flaky, known, duration, labels, history, retries, attachments, stop } = testResult ?? {}; const formattedDuration = formatDuration(duration); const fullDate = stop && timestampToDate(stop); const severity = labels?.find((label) => label.name === "severity")?.value ?? "normal"; const { t } = useI18n("ui"); + const statuses = Object.entries({ flaky, muted, known }).filter(([, value]) => value); const Content = () => { return ( @@ -37,6 +38,7 @@ export const TestResultInfo: FunctionalComponent = ({ testR {Boolean(status) && } {Boolean(history?.length) && } + {Boolean(statuses.length) && } {formattedDuration} diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultInfo/styles.scss b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/styles.scss index 2afbcab..6f7fbfd 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultInfo/styles.scss +++ b/packages/web-awesome/src/components/app/TestResult/TestResultInfo/styles.scss @@ -31,3 +31,20 @@ gap: 4px; align-items: center; } + +.test-result-info-statuses { + display: flex; + align-items: center; + gap: 8px; +} + +.test-result-info-status { + display: flex; + align-items: center; + gap: 2px; +} + +.metadata-icon { + margin-right: 8px; + color: var(--on-icon-secondary); +} diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultNavigation/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultNavigation/index.tsx index 9cbcc2c..b8a05e3 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultNavigation/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultNavigation/index.tsx @@ -44,11 +44,10 @@ export const TestResultNavigation: FunctionalComponent { const currentIndex = data.indexOf(id) + 1; - return (
{fullName && } - {data && ( + {data && !testResult?.hidden && (
1}> { + const { setCurrentTab } = useTestResultTabsContext(); return ( -
+
setCurrentTab("history")}>
); @@ -41,7 +41,7 @@ export const TestResultPrevStatuses: FunctionalComponent {history?.slice(0, 6).map((item, key) => ( -
+
}> diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultTeardown/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultTeardown/index.tsx index 1cb3e94..1fde26f 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultTeardown/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultTeardown/index.tsx @@ -1,6 +1,6 @@ -import { FunctionalComponent } from "preact"; +import type { FunctionalComponent } from "preact"; import { useState } from "preact/hooks"; -import { AllureAwesomeTestResult } from "types"; +import type { AllureAwesomeTestResult } from "types"; import lineHelpersFlag from "@/assets/svg/line-helpers-flag.svg"; import { TestResultDropdown } from "@/components/app/TestResult/TestResultDropdown"; import * as styles from "@/components/app/TestResult/TestResultSteps/styles.scss"; diff --git a/packages/web-awesome/src/components/app/TestResult/index.tsx b/packages/web-awesome/src/components/app/TestResult/index.tsx index 938177a..46ab54d 100644 --- a/packages/web-awesome/src/components/app/TestResult/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/index.tsx @@ -1,5 +1,5 @@ -import { FunctionComponent, FunctionalComponent } from "preact"; -import { AllureAwesomeTestResult } from "types"; +import type { FunctionComponent, FunctionalComponent } from "preact"; +import type { AllureAwesomeTestResult } from "types"; import * as styles from "@/components/app/BaseLayout/styles.scss"; import { TestResultAttachmentView } from "@/components/app/TestResult/TestResultAttachmentsView"; import TestResultEmpty from "@/components/app/TestResult/TestResultEmpty"; diff --git a/packages/web-awesome/src/components/app/Tree/TreeItem.tsx b/packages/web-awesome/src/components/app/Tree/TreeItem.tsx index 0044de3..30ea058 100644 --- a/packages/web-awesome/src/components/app/Tree/TreeItem.tsx +++ b/packages/web-awesome/src/components/app/Tree/TreeItem.tsx @@ -19,7 +19,9 @@ export const TreeItem: FunctionComponent = ({ name, groupOrder, s return (
navigateTo(id)}> - #{groupOrder} + + {groupOrder} + {name} diff --git a/packages/web-awesome/src/components/app/Tree/styles.scss b/packages/web-awesome/src/components/app/Tree/styles.scss index 95d60d7..4d84151 100644 --- a/packages/web-awesome/src/components/app/Tree/styles.scss +++ b/packages/web-awesome/src/components/app/Tree/styles.scss @@ -49,7 +49,6 @@ padding: 6px 8px 6px 6px; transition: background-color 300ms; gap: 4px; - align-items: center; cursor: pointer; position: relative; @@ -173,4 +172,11 @@ .order { user-select: none; + color: var(--on-text-hint); + min-width: 16px; + text-align: center; + box-sizing: content-box; + padding-top: 2px; + line-height: 16px; + width: 24px; } diff --git a/packages/web-awesome/src/components/commons/Button/styles.scss b/packages/web-awesome/src/components/commons/Button/styles.scss index ce2251e..8678dc1 100644 --- a/packages/web-awesome/src/components/commons/Button/styles.scss +++ b/packages/web-awesome/src/components/commons/Button/styles.scss @@ -290,6 +290,12 @@ &:hover { --allure-btn-bg-color: var(--bg-control-flat-medium); + + .contentIcon, + .leadingIcon, + .dropdownIcon { + color: var(--allure-btn-icon-color); + } } &:active, @@ -520,10 +526,6 @@ transition: color var(--color-change-transition-duration); user-select: none; - &:hover { - color: var(--allure-btn-icon-color); - } - &:focus { outline: none; } diff --git a/packages/web-awesome/src/i18n/constants.ts b/packages/web-awesome/src/i18n/constants.ts index d4fc4ad..322ad56 100644 --- a/packages/web-awesome/src/i18n/constants.ts +++ b/packages/web-awesome/src/i18n/constants.ts @@ -63,8 +63,8 @@ export const LANG_LOCALE: Record< }, am: { short: "Am", - full: "አማርኛ", - iso: "am-ET", + full: "Հայերեն", + iso: "hy-AM", }, az: { short: "Az", diff --git a/packages/web-awesome/src/i18n/locales/am.json b/packages/web-awesome/src/i18n/locales/am.json index e9eb0e4..d4c9b88 100644 --- a/packages/web-awesome/src/i18n/locales/am.json +++ b/packages/web-awesome/src/i18n/locales/am.json @@ -109,7 +109,8 @@ "collapse": "Կծկել", "expand": "Ընդլայնել", "fullscreen": "Լրիվ էկրան", - "language": "Փոխել լեզուն" + "language": "Փոխել լեզուն", + "openInNewTab": "Բացել նոր ներդիրում" }, "errors": { "missedAttachment": "Կցորդը չի գտնվել" diff --git a/packages/web-awesome/src/i18n/locales/az.json b/packages/web-awesome/src/i18n/locales/az.json index d191677..ec1eda7 100644 --- a/packages/web-awesome/src/i18n/locales/az.json +++ b/packages/web-awesome/src/i18n/locales/az.json @@ -109,7 +109,8 @@ "collapse": "Daralt", "expand": "Genişləndir", "fullscreen": "Tam ekran", - "language": "Dili dəyiş" + "language": "Dili dəyiş", + "openInNewTab": "Yeni tabda aç" }, "errors": { "missedAttachment": "Əlavə tapılmadı" diff --git a/packages/web-awesome/src/i18n/locales/de.json b/packages/web-awesome/src/i18n/locales/de.json index fef6c9d..fd1cc08 100644 --- a/packages/web-awesome/src/i18n/locales/de.json +++ b/packages/web-awesome/src/i18n/locales/de.json @@ -109,7 +109,8 @@ "collapse": "Einklappen", "expand": "Ausklappen", "fullscreen": "Vollbild", - "language": "Sprache ändern" + "language": "Sprache ändern", + "openInNewTab": "In neuem Tab öffnen" }, "errors": { "missedAttachment": "Anhang nicht gefunden" diff --git a/packages/web-awesome/src/i18n/locales/en.json b/packages/web-awesome/src/i18n/locales/en.json index cd5d57f..c823415 100644 --- a/packages/web-awesome/src/i18n/locales/en.json +++ b/packages/web-awesome/src/i18n/locales/en.json @@ -109,7 +109,8 @@ "collapse": "Collapse", "expand": "Expand", "fullscreen": "Full screen", - "language": "Change language" + "language": "Change language", + "openInNewTab": "Open in new tab" }, "errors": { "missedAttachment": "Attachment not found" diff --git a/packages/web-awesome/src/i18n/locales/es.json b/packages/web-awesome/src/i18n/locales/es.json index 2394338..fc8ed0b 100644 --- a/packages/web-awesome/src/i18n/locales/es.json +++ b/packages/web-awesome/src/i18n/locales/es.json @@ -109,7 +109,8 @@ "collapse": "Colapsar", "expand": "Expandir", "fullscreen": "Pantalla completa", - "language": "Cambiar idioma" + "language": "Cambiar idioma", + "openInNewTab": "Abrir en nueva pestaña" }, "errors": { "missedAttachment": "Adjunto no encontrado" diff --git a/packages/web-awesome/src/i18n/locales/fr.json b/packages/web-awesome/src/i18n/locales/fr.json index edbb94f..23c8567 100644 --- a/packages/web-awesome/src/i18n/locales/fr.json +++ b/packages/web-awesome/src/i18n/locales/fr.json @@ -109,7 +109,8 @@ "collapse": "Réduire", "expand": "Agrandir", "fullscreen": "Plein écran", - "language": "Changer la langue" + "language": "Changer la langue", + "openInNewTab": "Ouvrir dans un nouvel onglet" }, "errors": { "missedAttachment": "Pièce jointe non trouvée" diff --git a/packages/web-awesome/src/i18n/locales/he.json b/packages/web-awesome/src/i18n/locales/he.json index 468de90..ed329ce 100644 --- a/packages/web-awesome/src/i18n/locales/he.json +++ b/packages/web-awesome/src/i18n/locales/he.json @@ -109,7 +109,8 @@ "collapse": "כווץ", "expand": "הרחב", "fullscreen": "מסך מלא", - "language": "שנה שפה" + "language": "שנה שפה", + "openInNewTab": "פתח בכרטיסייה חדשה" }, "errors": { "missedAttachment": "הקובץ המצורף לא נמצא" diff --git a/packages/web-awesome/src/i18n/locales/it.json b/packages/web-awesome/src/i18n/locales/it.json index 21ee6f3..55a16ee 100644 --- a/packages/web-awesome/src/i18n/locales/it.json +++ b/packages/web-awesome/src/i18n/locales/it.json @@ -109,7 +109,8 @@ "collapse": "Comprimi", "expand": "Espandi", "fullscreen": "Schermo intero", - "language": "Cambia lingua" + "language": "Cambia lingua", + "openInNewTab": "Apri in una nuova scheda" }, "errors": { "missedAttachment": "Allegato non trovato" diff --git a/packages/web-awesome/src/i18n/locales/ja.json b/packages/web-awesome/src/i18n/locales/ja.json index 421b9b6..895cf42 100644 --- a/packages/web-awesome/src/i18n/locales/ja.json +++ b/packages/web-awesome/src/i18n/locales/ja.json @@ -109,7 +109,8 @@ "collapse": "折りたたむ", "expand": "展開", "fullscreen": "全画面", - "language": "言語を変更" + "language": "言語を変更", + "openInNewTab": "新しいタブで開く" }, "errors": { "missedAttachment": "添付ファイルが見つかりません" diff --git a/packages/web-awesome/src/i18n/locales/ka.json b/packages/web-awesome/src/i18n/locales/ka.json index f0284ea..220e5ed 100644 --- a/packages/web-awesome/src/i18n/locales/ka.json +++ b/packages/web-awesome/src/i18n/locales/ka.json @@ -109,7 +109,8 @@ "collapse": "ჩაკეცვა", "expand": "გახსნა", "fullscreen": "სრულ ეკრანზე", - "language": "ენის შეცვლა" + "language": "ენის შეცვლა", + "openInNewTab": "გახსნა ახალ ჩანართში" }, "errors": { "missedAttachment": "დანართი ვერ მოიძებნა" diff --git a/packages/web-awesome/src/i18n/locales/kr.json b/packages/web-awesome/src/i18n/locales/kr.json index 7c43a57..a181447 100644 --- a/packages/web-awesome/src/i18n/locales/kr.json +++ b/packages/web-awesome/src/i18n/locales/kr.json @@ -109,7 +109,8 @@ "collapse": "접기", "expand": "펼치기", "fullscreen": "전체 화면", - "language": "언어 변경" + "language": "언어 변경", + "openInNewTab": "새 탭에서 열기" }, "errors": { "missedAttachment": "첨부파일을 찾을 수 없습니다" diff --git a/packages/web-awesome/src/i18n/locales/nl.json b/packages/web-awesome/src/i18n/locales/nl.json index 4a76a3b..8478d08 100644 --- a/packages/web-awesome/src/i18n/locales/nl.json +++ b/packages/web-awesome/src/i18n/locales/nl.json @@ -109,7 +109,8 @@ "collapse": "Samenvouwen", "expand": "Uitvouwen", "fullscreen": "Volledig scherm", - "language": "Taal wijzigen" + "language": "Taal wijzigen", + "openInNewTab": "Openen in een nieuw tabblad" }, "errors": { "missedAttachment": "Bijlage niet gevonden" diff --git a/packages/web-awesome/src/i18n/locales/pl.json b/packages/web-awesome/src/i18n/locales/pl.json index 708f601..f05772c 100644 --- a/packages/web-awesome/src/i18n/locales/pl.json +++ b/packages/web-awesome/src/i18n/locales/pl.json @@ -107,7 +107,8 @@ "collapse": "Skrócić", "expand": "Poszerzyć", "fullscreen": "Pełny ekran", - "language": "Zmień język" + "language": "Zmień język", + "openInNewTab": "Otwórz w nowej karcie" }, "errors": { "missedAttachment": "Nie znaleziono załącznika" diff --git a/packages/web-awesome/src/i18n/locales/pt.json b/packages/web-awesome/src/i18n/locales/pt.json index 3998866..fad05fd 100644 --- a/packages/web-awesome/src/i18n/locales/pt.json +++ b/packages/web-awesome/src/i18n/locales/pt.json @@ -109,7 +109,8 @@ "collapse": "Recolher", "expand": "Expandir", "fullscreen": "Tela cheia", - "language": "Alterar idioma" + "language": "Alterar idioma", + "openInNewTab": "Abrir em nova aba" }, "errors": { "missedAttachment": "Anexo não encontrado" diff --git a/packages/web-awesome/src/i18n/locales/ru.json b/packages/web-awesome/src/i18n/locales/ru.json index 3b69942..ba54321 100644 --- a/packages/web-awesome/src/i18n/locales/ru.json +++ b/packages/web-awesome/src/i18n/locales/ru.json @@ -107,7 +107,8 @@ "collapse": "Свернуть", "expand": "Развернуть", "fullscreen": "На весь экран", - "language": "Сменить язык" + "language": "Сменить язык", + "openInNewTab": "Открыть в новой вкладке" }, "errors": { "missedAttachment": "Вложение не найдено" diff --git a/packages/web-awesome/src/i18n/locales/sv.json b/packages/web-awesome/src/i18n/locales/sv.json index 1aebcfe..ccf9cb0 100644 --- a/packages/web-awesome/src/i18n/locales/sv.json +++ b/packages/web-awesome/src/i18n/locales/sv.json @@ -109,7 +109,8 @@ "collapse": "Minimera", "expand": "Expandera", "fullscreen": "Helskärm", - "language": "Byt språk" + "language": "Byt språk", + "openInNewTab": "Öppna i ny flik" }, "errors": { "missedAttachment": "Bilaga hittades inte" diff --git a/packages/web-awesome/src/i18n/locales/tr.json b/packages/web-awesome/src/i18n/locales/tr.json index 42dbea0..4ef19a8 100644 --- a/packages/web-awesome/src/i18n/locales/tr.json +++ b/packages/web-awesome/src/i18n/locales/tr.json @@ -109,7 +109,8 @@ "collapse": "Daralt", "expand": "Genişlet", "fullscreen": "Tam ekran", - "language": "Dili değiştir" + "language": "Dili değiştir", + "openInNewTab": "Yeni sekmede aç" }, "errors": { "missedAttachment": "Ek bulunamadı" diff --git a/packages/web-awesome/src/i18n/locales/zh.json b/packages/web-awesome/src/i18n/locales/zh.json index 7e0b8f4..7ab31e7 100644 --- a/packages/web-awesome/src/i18n/locales/zh.json +++ b/packages/web-awesome/src/i18n/locales/zh.json @@ -109,7 +109,8 @@ "collapse": "折叠", "expand": "展开", "fullscreen": "全屏", - "language": "更改语言" + "language": "更改语言", + "openInNewTab": "在新标签页中打开" }, "errors": { "missedAttachment": "未找到附件" diff --git a/packages/web-awesome/src/index.tsx b/packages/web-awesome/src/index.tsx index e5ec2ce..75320e2 100644 --- a/packages/web-awesome/src/index.tsx +++ b/packages/web-awesome/src/index.tsx @@ -35,6 +35,10 @@ export const navigateTo = (path: string) => { globalThis.location.hash = path; }; +export const openInNewTab = (path: string) => { + window.open(`#${path}`, "_blank"); +}; + const rootElement = document.getElementById("app"); document.addEventListener("DOMContentLoaded", () => { diff --git a/packages/web-awesome/src/stores/stats.ts b/packages/web-awesome/src/stores/stats.ts index 3bf44b4..1c9ff42 100644 --- a/packages/web-awesome/src/stores/stats.ts +++ b/packages/web-awesome/src/stores/stats.ts @@ -1,7 +1,7 @@ import type { Statistic } from "@allurereport/core-api"; import { fetchReportJsonData } from "@allurereport/web-commons"; import { signal } from "@preact/signals"; -import { StoreSignalState } from "@/stores/types"; +import type { StoreSignalState } from "@/stores/types"; export const statsStore = signal>({ loading: true, diff --git a/packages/web-awesome/src/utils/copyToClipboard.ts b/packages/web-awesome/src/utils/copyToClipboard.ts index 7225f31..a5ddb45 100644 --- a/packages/web-awesome/src/utils/copyToClipboard.ts +++ b/packages/web-awesome/src/utils/copyToClipboard.ts @@ -1,3 +1,16 @@ export const copyToClipboard = async (text: string) => { - await navigator.clipboard.writeText(text); + if (navigator.clipboard) { + await navigator.clipboard.writeText(text); + return; + } + + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; + textarea.style.opacity = "0"; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + document.execCommand("copy"); + document.body.removeChild(textarea); }; From d014ca971814f825a18fe4297938676f3bc542bb Mon Sep 17 00:00:00 2001 From: Todti Date: Thu, 9 Jan 2025 11:45:41 +0100 Subject: [PATCH 2/3] fix: after review --- .../src/components/app/ArrowButton/styles.scss | 1 + .../TestResultHistory/TestResultHistoryItem.tsx | 16 +++++++++++++--- .../TestResult/TestResultPrevStatuses/index.tsx | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/web-awesome/src/components/app/ArrowButton/styles.scss b/packages/web-awesome/src/components/app/ArrowButton/styles.scss index 36d1c5a..4c4db85 100644 --- a/packages/web-awesome/src/components/app/ArrowButton/styles.scss +++ b/packages/web-awesome/src/components/app/ArrowButton/styles.scss @@ -3,6 +3,7 @@ border: none; padding: 8px 4px; border-radius: 4px; + cursor: pointer; color: var(--on-icon-secondary); &:hover { diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx index 146ac96..82edc02 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx @@ -24,9 +24,19 @@ export const TestResultHistoryItem = ({ testResultItem }) => { return (
-
setIsOpen(!isOpened)}> - {Boolean(message) && } -
+
+ {Boolean(message) && ( + setIsOpen(!isOpened)}> + + + )} +
{ + e.stopPropagation(); + openInNewTab(navigateUrl); + }} + > {convertedStop}
diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx index a6c3f82..092a8e6 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx @@ -5,15 +5,15 @@ import { useTestResultTabsContext } from "@/components/app/TestResult/TestResult import { SvgIcon } from "@/components/commons/SvgIcon"; import { TooltipWrapper } from "@/components/commons/Tooltip"; import { Text } from "@/components/commons/Typography"; +import { openInNewTab } from "@/index"; import { useI18n } from "@/stores"; import { capitalize } from "@/utils/capitalize"; import { timestampToDate } from "@/utils/time"; import * as styles from "./styles.scss"; const TestResultPrevStatus = ({ item }) => { - const { setCurrentTab } = useTestResultTabsContext(); return ( -
setCurrentTab("history")}> +
openInNewTab(`testresult/${item.id}`)}>
); From b273219881c3727b24ff100c18f940005553278e Mon Sep 17 00:00:00 2001 From: Todti Date: Thu, 9 Jan 2025 16:34:52 +0100 Subject: [PATCH 3/3] fix: after review --- .pnp.cjs | 23 +++++++++++++++++- ...ilter-npm-0.0.10-28e06ce546-1e45182f42.zip | Bin 0 -> 10147 bytes .../xss-npm-1.0.15-a70a62f810-074ad54bab.zip | Bin 0 -> 41943 bytes .../app/TestResult/TestResultError/index.tsx | 5 +++- .../TestResultHistoryItem.tsx | 4 +-- .../TestResultPrevStatuses/index.tsx | 4 +-- packages/web-commons/package.json | 3 ++- packages/web-commons/src/index.ts | 1 + packages/web-commons/src/sanitizeHtml.ts | 12 +++++++++ yarn.lock | 22 ++++++++++++++++- 10 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 .yarn/cache/cssfilter-npm-0.0.10-28e06ce546-1e45182f42.zip create mode 100644 .yarn/cache/xss-npm-1.0.15-a70a62f810-074ad54bab.zip create mode 100644 packages/web-commons/src/sanitizeHtml.ts diff --git a/.pnp.cjs b/.pnp.cjs index 647160a..6756592 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -851,7 +851,8 @@ const RAW_RUNTIME_STATE = ["rimraf", "npm:6.0.1"],\ ["tslib", "npm:2.7.0"],\ ["typescript", "patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=8c6c40"],\ - ["vitest", "virtual:9b037461298e50c6c170aa1fff242bb8930ef8feec399d8aa7e79f6479e91f7b3bf5d631b0c7f7701b011ebd07a5efa42e663e604109236dbee18c18976c43dd#npm:2.1.8"]\ + ["vitest", "virtual:9b037461298e50c6c170aa1fff242bb8930ef8feec399d8aa7e79f6479e91f7b3bf5d631b0c7f7701b011ebd07a5efa42e663e604109236dbee18c18976c43dd#npm:2.1.8"],\ + ["xss", "npm:1.0.15"]\ ],\ "linkType": "SOFT"\ }]\ @@ -10675,6 +10676,15 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["cssfilter", [\ + ["npm:0.0.10", {\ + "packageLocation": "./.yarn/cache/cssfilter-npm-0.0.10-28e06ce546-1e45182f42.zip/node_modules/cssfilter/",\ + "packageDependencies": [\ + ["cssfilter", "npm:0.0.10"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["cssnano", [\ ["npm:6.1.2", {\ "packageLocation": "./.yarn/cache/cssnano-npm-6.1.2-9634759bd9-65aad92c5e.zip/node_modules/cssnano/",\ @@ -21534,6 +21544,17 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["xss", [\ + ["npm:1.0.15", {\ + "packageLocation": "./.yarn/cache/xss-npm-1.0.15-a70a62f810-074ad54bab.zip/node_modules/xss/",\ + "packageDependencies": [\ + ["xss", "npm:1.0.15"],\ + ["commander", "npm:2.20.3"],\ + ["cssfilter", "npm:0.0.10"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["yallist", [\ ["npm:3.1.1", {\ "packageLocation": "./.yarn/cache/yallist-npm-3.1.1-a568a556b4-9af0a4329c.zip/node_modules/yallist/",\ diff --git a/.yarn/cache/cssfilter-npm-0.0.10-28e06ce546-1e45182f42.zip b/.yarn/cache/cssfilter-npm-0.0.10-28e06ce546-1e45182f42.zip new file mode 100644 index 0000000000000000000000000000000000000000..2ea5490d7ef81b56320204a577a08be522338ff9 GIT binary patch literal 10147 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%0D^0=0YCRf5Y|%2HTbgp(>G?^b4@ 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 literal 0 HcmV?d00001 diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx index 6e6d910..cc80e80 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultError/index.tsx @@ -1,3 +1,4 @@ +import { sanitizeHtml } from "@allurereport/web-commons"; import { useState } from "preact/hooks"; import LineGeneralCopy3 from "@/assets/svg/line-general-copy-3.svg"; import { IconButton } from "@/components/commons/Button"; @@ -8,10 +9,12 @@ import { copyToClipboard } from "@/utils/copyToClipboard"; import * as styles from "./styles.scss"; const TestResultErrorTrace = ({ trace }) => { + const sanitizedTrace = sanitizeHtml(trace); + return (
-
{trace}
+
       
     
); diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx index 82edc02..74f44f2 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx @@ -9,7 +9,7 @@ import TreeItemIcon from "@/components/app/Tree/TreeItemIcon"; import { IconButton } from "@/components/commons/Button"; import { TooltipWrapper } from "@/components/commons/Tooltip"; import { Text } from "@/components/commons/Typography"; -import { openInNewTab } from "@/index"; +import { navigateTo, openInNewTab } from "@/index"; import { useI18n } from "@/stores"; import { timestampToDate } from "@/utils/time"; @@ -34,7 +34,7 @@ export const TestResultHistoryItem = ({ testResultItem }) => { className={styles["test-result-history-item-wrap"]} onClick={(e) => { e.stopPropagation(); - openInNewTab(navigateUrl); + navigateTo(navigateUrl); }} > diff --git a/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx b/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx index 092a8e6..d2ea1c7 100644 --- a/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx +++ b/packages/web-awesome/src/components/app/TestResult/TestResultPrevStatuses/index.tsx @@ -5,7 +5,7 @@ import { useTestResultTabsContext } from "@/components/app/TestResult/TestResult import { SvgIcon } from "@/components/commons/SvgIcon"; import { TooltipWrapper } from "@/components/commons/Tooltip"; import { Text } from "@/components/commons/Typography"; -import { openInNewTab } from "@/index"; +import { navigateTo, openInNewTab } from "@/index"; import { useI18n } from "@/stores"; import { capitalize } from "@/utils/capitalize"; import { timestampToDate } from "@/utils/time"; @@ -13,7 +13,7 @@ import * as styles from "./styles.scss"; const TestResultPrevStatus = ({ item }) => { return ( -
openInNewTab(`testresult/${item.id}`)}> +
navigateTo(`testresult/${item.id}`)}>
); diff --git a/packages/web-commons/package.json b/packages/web-commons/package.json index 7d4a2f5..7ae9e07 100644 --- a/packages/web-commons/package.json +++ b/packages/web-commons/package.json @@ -43,6 +43,7 @@ "rimraf": "^6.0.1", "tslib": "^2.7.0", "typescript": "^5.6.3", - "vitest": "^2.1.8" + "vitest": "^2.1.8", + "xss": "^1.0.15" } } diff --git a/packages/web-commons/src/index.ts b/packages/web-commons/src/index.ts index 696b109..1865d55 100644 --- a/packages/web-commons/src/index.ts +++ b/packages/web-commons/src/index.ts @@ -1,2 +1,3 @@ export * from "./data.js"; export * from "./static.js"; +export * from "./sanitizeHtml.js"; diff --git a/packages/web-commons/src/sanitizeHtml.ts b/packages/web-commons/src/sanitizeHtml.ts new file mode 100644 index 0000000..44100b5 --- /dev/null +++ b/packages/web-commons/src/sanitizeHtml.ts @@ -0,0 +1,12 @@ +import xss from "xss"; + +export const sanitizeHtml = (html: string) => { + return xss(html, { + stripIgnoreTagBody: ["script"], + whiteList: { + div: ["style"], + span: ["style"], + }, + css: true, + }); +}; diff --git a/yarn.lock b/yarn.lock index 23fc18c..630e546 100644 --- a/yarn.lock +++ b/yarn.lock @@ -665,6 +665,7 @@ __metadata: tslib: "npm:^2.7.0" typescript: "npm:^5.6.3" vitest: "npm:^2.1.8" + xss: "npm:^1.0.15" languageName: unknown linkType: soft @@ -6112,7 +6113,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^2.20.0": +"commander@npm:^2.20.0, commander@npm:^2.20.3": version: 2.20.3 resolution: "commander@npm:2.20.3" checksum: 10/90c5b6898610cd075984c58c4f88418a4fb44af08c1b1415e9854c03171bec31b336b7f3e4cefe33de994b3f12b03c5e2d638da4316df83593b9e82554e7e95b @@ -6434,6 +6435,13 @@ __metadata: languageName: node linkType: hard +"cssfilter@npm:0.0.10": + version: 0.0.10 + resolution: "cssfilter@npm:0.0.10" + checksum: 10/1e45182f42de848f092f50a313113c28a88e4ac98333bf1603ee1c3b200384a3bc83c12e35cd61135e3b0f218295f600d51120ca1f926b7958b2d3262d711214 + languageName: node + linkType: hard + "cssnano-preset-default@npm:^6.1.2": version: 6.1.2 resolution: "cssnano-preset-default@npm:6.1.2" @@ -15209,6 +15217,18 @@ __metadata: languageName: node linkType: hard +"xss@npm:^1.0.15": + version: 1.0.15 + resolution: "xss@npm:1.0.15" + dependencies: + commander: "npm:^2.20.3" + cssfilter: "npm:0.0.10" + bin: + xss: bin/xss + checksum: 10/074ad54babac9dd5107466dbf30d3b871dbedae1f8e7b8f4e3b76d60da8b92bd0f66f18ccd26b8524545444ef784b78c526cee089a907aa904f83c8b8d7958f6 + languageName: node + linkType: hard + "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1"