diff --git a/packages/html-reporter/src/testCaseView.tsx b/packages/html-reporter/src/testCaseView.tsx index aa85522a8fd01..2e8e71cf11b2c 100644 --- a/packages/html-reporter/src/testCaseView.tsx +++ b/packages/html-reporter/src/testCaseView.tsx @@ -25,7 +25,7 @@ import { statusIcon } from './statusIcon'; import './testCaseView.css'; import { TestResultView } from './testResultView'; import { linkifyText } from '@web/renderUtils'; -import { msToString } from './utils'; +import { msToString } from '@isomorphic/formatUtils'; import { clsx } from '@web/uiUtils'; import { CopyToClipboardContainer } from './copyToClipboard'; import { HeaderView } from './headerView'; diff --git a/packages/html-reporter/src/testFileView.tsx b/packages/html-reporter/src/testFileView.tsx index 4022094bf6700..688e1b2f8b799 100644 --- a/packages/html-reporter/src/testFileView.tsx +++ b/packages/html-reporter/src/testFileView.tsx @@ -16,7 +16,7 @@ import type { TestCaseSummary, TestFileSummary } from './types'; import * as React from 'react'; -import { msToString } from './utils'; +import { msToString } from '@isomorphic/formatUtils'; import { Chip } from './chip'; import { Link, LinkBadge, testResultHref, TraceLink, useSearchParams } from './links'; import { statusIcon } from './statusIcon'; diff --git a/packages/html-reporter/src/testFilesView.tsx b/packages/html-reporter/src/testFilesView.tsx index cf6825108ef7c..84dbc988093bd 100644 --- a/packages/html-reporter/src/testFilesView.tsx +++ b/packages/html-reporter/src/testFilesView.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import { TestFileView } from './testFileView'; import './testFileView.css'; import './chip.css'; -import { msToString } from './utils'; +import { msToString } from '@isomorphic/formatUtils'; import { Chip } from './chip'; import { CodeSnippet } from './testErrorView'; import * as icons from './icons'; diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index cb8e6313978c6..11ebc4835a724 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -17,7 +17,8 @@ import type { TestAttachment, TestCase, TestCaseSummary, TestResult, TestStep } from './types'; import * as React from 'react'; import { TreeItem } from './treeItem'; -import { formatUrl, msToString } from './utils'; +import { formatUrl } from './utils'; +import { msToString } from '@isomorphic/formatUtils'; import { AutoChip } from './chip'; import { traceImage } from './images'; import { Anchor, AttachmentLink, generateTraceUrl, testResultHref, useSearchParams } from './links'; diff --git a/packages/html-reporter/src/utils.ts b/packages/html-reporter/src/utils.ts index 2737b28311de0..9ea7a26c1ef12 100644 --- a/packages/html-reporter/src/utils.ts +++ b/packages/html-reporter/src/utils.ts @@ -14,32 +14,6 @@ limitations under the License. */ -export function msToString(ms: number): string { - if (!isFinite(ms)) - return '-'; - - if (ms === 0) - return '0ms'; - - if (ms < 1000) - return ms.toFixed(0) + 'ms'; - - const seconds = ms / 1000; - if (seconds < 60) - return seconds.toFixed(1) + 's'; - - const minutes = seconds / 60; - if (minutes < 60) - return minutes.toFixed(1) + 'm'; - - const hours = minutes / 60; - if (hours < 24) - return hours.toFixed(1) + 'h'; - - const days = hours / 24; - return days.toFixed(1) + 'd'; -} - // hash string to integer in range [0, 6] for color index, to get same color for same tag export function hashStringToInt(str: string) { let hash = 0; diff --git a/packages/playwright-core/src/tools/trace/traceCli.ts b/packages/playwright-core/src/tools/trace/traceCli.ts index e08111364937e..f51ece2e534a5 100644 --- a/packages/playwright-core/src/tools/trace/traceCli.ts +++ b/packages/playwright-core/src/tools/trace/traceCli.ts @@ -23,6 +23,7 @@ import { TraceModel, buildActionTree } from '../../utils/isomorphic/trace/traceM import { TraceLoader } from '../../utils/isomorphic/trace/traceLoader'; import { renderTitleForCall } from '../../utils/isomorphic/protocolFormatter'; import { asLocatorDescription } from '../../utils/isomorphic/locatorGenerators'; +import { msToString, bytesToString } from '../../utils/isomorphic/formatUtils'; import { ZipTraceLoaderBackend } from './traceParser'; import type { ActionTraceEventInContext } from '@isomorphic/trace/traceModel'; @@ -149,43 +150,6 @@ export async function loadTraceModel(traceFile: string): Promise { return (await loadTrace(traceFile)).model; } -function msToString(ms: number): string { - if (ms < 0 || !isFinite(ms)) - return '-'; - if (ms === 0) - return '0'; - if (ms < 1000) - return ms.toFixed(0) + 'ms'; - const seconds = ms / 1000; - if (seconds < 60) - return seconds.toFixed(1) + 's'; - const minutes = seconds / 60; - if (minutes < 60) - return minutes.toFixed(1) + 'm'; - const hours = minutes / 60; - if (hours < 24) - return hours.toFixed(1) + 'h'; - const days = hours / 24; - return days.toFixed(1) + 'd'; -} - -function bytesToString(bytes: number): string { - if (bytes < 0 || !isFinite(bytes)) - return '-'; - if (bytes === 0) - return '0'; - if (bytes < 1000) - return bytes.toFixed(0); - const kb = bytes / 1024; - if (kb < 1000) - return kb.toFixed(1) + 'K'; - const mb = kb / 1024; - if (mb < 1000) - return mb.toFixed(1) + 'M'; - const gb = mb / 1024; - return gb.toFixed(1) + 'G'; -} - function formatTimestamp(ms: number, base: number): string { const relative = ms - base; if (relative < 0) diff --git a/packages/playwright-core/src/utils.ts b/packages/playwright-core/src/utils.ts index a1ef2907198e4..405ea86d5d95b 100644 --- a/packages/playwright-core/src/utils.ts +++ b/packages/playwright-core/src/utils.ts @@ -29,6 +29,7 @@ export * from './utils/isomorphic/rtti'; export * from './utils/isomorphic/semaphore'; export * from './utils/isomorphic/stackTrace'; export * from './utils/isomorphic/stringUtils'; +export * from './utils/isomorphic/formatUtils'; export * from './utils/isomorphic/time'; export * from './utils/isomorphic/timeoutRunner'; export * from './utils/isomorphic/urlMatch'; diff --git a/packages/playwright-core/src/utils/isomorphic/formatUtils.ts b/packages/playwright-core/src/utils/isomorphic/formatUtils.ts new file mode 100644 index 0000000000000..b03a9e3bb180a --- /dev/null +++ b/packages/playwright-core/src/utils/isomorphic/formatUtils.ts @@ -0,0 +1,63 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function msToString(ms: number): string { + if (ms < 0 || !isFinite(ms)) + return '-'; + + if (ms === 0) + return '0ms'; + + if (ms < 1000) + return ms.toFixed(0) + 'ms'; + + const seconds = ms / 1000; + if (seconds < 60) + return seconds.toFixed(1) + 's'; + + const minutes = seconds / 60; + if (minutes < 60) + return minutes.toFixed(1) + 'm'; + + const hours = minutes / 60; + if (hours < 24) + return hours.toFixed(1) + 'h'; + + const days = hours / 24; + return days.toFixed(1) + 'd'; +} + +export function bytesToString(bytes: number): string { + if (bytes < 0 || !isFinite(bytes)) + return '-'; + + if (bytes === 0) + return '0'; + + if (bytes < 1000) + return bytes.toFixed(0); + + const kb = bytes / 1024; + if (kb < 1000) + return kb.toFixed(1) + 'K'; + + const mb = kb / 1024; + if (mb < 1000) + return mb.toFixed(1) + 'M'; + + const gb = mb / 1024; + return gb.toFixed(1) + 'G'; +} diff --git a/packages/playwright-core/src/utilsBundle.ts b/packages/playwright-core/src/utilsBundle.ts index 2eab1d9872899..e22bf61a8b820 100644 --- a/packages/playwright-core/src/utilsBundle.ts +++ b/packages/playwright-core/src/utilsBundle.ts @@ -39,29 +39,3 @@ export const yaml: typeof import('../bundles/utils/node_modules/yaml') = require export type { Range as YAMLRange, Scalar as YAMLScalar, YAMLError, YAMLMap, YAMLSeq } from '../bundles/utils/node_modules/yaml'; export type { Command } from '../bundles/utils/node_modules/commander'; export type { EventEmitter as WebSocketEventEmitter, RawData as WebSocketRawData, WebSocket, WebSocketServer } from '../bundles/utils/node_modules/@types/ws'; - -export function ms(ms: number): string { - if (!isFinite(ms)) - return '-'; - - if (ms === 0) - return '0ms'; - - if (ms < 1000) - return ms.toFixed(0) + 'ms'; - - const seconds = ms / 1000; - if (seconds < 60) - return seconds.toFixed(1) + 's'; - - const minutes = seconds / 60; - if (minutes < 60) - return minutes.toFixed(1) + 'm'; - - const hours = minutes / 60; - if (hours < 24) - return hours.toFixed(1) + 'h'; - - const days = hours / 24; - return days.toFixed(1) + 'd'; -} diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index 200a975740972..f00437c477cdc 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -16,8 +16,7 @@ import path from 'path'; -import { getPackageManagerExecCommand, parseErrorStack } from 'playwright-core/lib/utils'; -import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; +import { getPackageManagerExecCommand, msToString as milliseconds, parseErrorStack } from 'playwright-core/lib/utils'; import { colors as realColors, noColors } from 'playwright-core/lib/utils'; import { ansiRegex, resolveReporterOutputPath, stripAnsiEscapes } from '../util'; diff --git a/packages/playwright/src/reporters/github.ts b/packages/playwright/src/reporters/github.ts index ceac0d89147b5..c19c7ab621347 100644 --- a/packages/playwright/src/reporters/github.ts +++ b/packages/playwright/src/reporters/github.ts @@ -16,8 +16,7 @@ import path from 'path'; -import { noColors } from 'playwright-core/lib/utils'; -import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; +import { msToString as milliseconds, noColors } from 'playwright-core/lib/utils'; import { TerminalReporter, formatResultFailure, formatRetry } from './base'; import { stripAnsiEscapes } from '../util'; diff --git a/packages/playwright/src/reporters/list.ts b/packages/playwright/src/reporters/list.ts index 9315e3bfa38c6..26216edababd2 100644 --- a/packages/playwright/src/reporters/list.ts +++ b/packages/playwright/src/reporters/list.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { getAsBooleanFromENV } from 'playwright-core/lib/utils'; -import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; +import { getAsBooleanFromENV, msToString as milliseconds } from 'playwright-core/lib/utils'; import { markErrorsAsReported, TerminalReporter, stepSuffix } from './base'; import { stripAnsiEscapes } from '../util'; diff --git a/packages/recorder/src/callLog.tsx b/packages/recorder/src/callLog.tsx index eafc9f9826816..bc28d82a673f5 100644 --- a/packages/recorder/src/callLog.tsx +++ b/packages/recorder/src/callLog.tsx @@ -17,7 +17,8 @@ import './callLog.css'; import * as React from 'react'; import type { CallLog } from './recorderTypes'; -import { clsx, msToString } from '@web/uiUtils'; +import { clsx } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import { asLocator } from '@isomorphic/locatorGenerators'; import type { Language } from '@isomorphic/locatorGenerators'; diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 56fb5e0e16319..7d666db23b648 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -15,7 +15,8 @@ */ import type { ActionTraceEvent } from '@trace/trace'; -import { clsx, msToString } from '@web/uiUtils'; +import { clsx } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import * as React from 'react'; import './actionList.css'; import { stats, buildActionTree } from '@isomorphic/trace/traceModel'; diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx index 223027fc8c01a..943ec4a897d4e 100644 --- a/packages/trace-viewer/src/ui/callTab.tsx +++ b/packages/trace-viewer/src/ui/callTab.tsx @@ -16,7 +16,8 @@ import type { SerializedValue } from '@protocol/channels'; import type { ActionTraceEvent } from '@trace/trace'; -import { clsx, msToString } from '@web/uiUtils'; +import { clsx } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import * as React from 'react'; import './callTab.css'; import { CopyToClipboard } from './copyToClipboard'; diff --git a/packages/trace-viewer/src/ui/consoleTab.tsx b/packages/trace-viewer/src/ui/consoleTab.tsx index 8194e973e4d44..1b88a15b956d5 100644 --- a/packages/trace-viewer/src/ui/consoleTab.tsx +++ b/packages/trace-viewer/src/ui/consoleTab.tsx @@ -20,7 +20,8 @@ import './consoleTab.css'; import type { TraceModel } from '@isomorphic/trace/traceModel'; import { ListView } from '@web/components/listView'; import type { Boundaries } from './geometry'; -import { clsx, msToString } from '@web/uiUtils'; +import { clsx } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import { ansi2html } from '@web/ansi2html'; import { PlaceholderPanel } from './placeholderPanel'; diff --git a/packages/trace-viewer/src/ui/logTab.tsx b/packages/trace-viewer/src/ui/logTab.tsx index a98c433855ff0..7f0ac6acfa9b6 100644 --- a/packages/trace-viewer/src/ui/logTab.tsx +++ b/packages/trace-viewer/src/ui/logTab.tsx @@ -18,7 +18,7 @@ import type { ActionTraceEventInContext } from '@isomorphic/trace/traceModel'; import * as React from 'react'; import { ListView } from '@web/components/listView'; import { PlaceholderPanel } from './placeholderPanel'; -import { msToString } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import './logTab.css'; const LogList = ListView<{ message: string, time: string }>; diff --git a/packages/trace-viewer/src/ui/metadataView.tsx b/packages/trace-viewer/src/ui/metadataView.tsx index 1a863f4c4ce53..4fc57799f021d 100644 --- a/packages/trace-viewer/src/ui/metadataView.tsx +++ b/packages/trace-viewer/src/ui/metadataView.tsx @@ -14,7 +14,7 @@ limitations under the License. */ -import { msToString } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import * as React from 'react'; import type { TraceModel } from '@isomorphic/trace/traceModel'; import './callTab.css'; diff --git a/packages/trace-viewer/src/ui/networkResourceDetails.tsx b/packages/trace-viewer/src/ui/networkResourceDetails.tsx index 4ca5efc71038f..63be975e26953 100644 --- a/packages/trace-viewer/src/ui/networkResourceDetails.tsx +++ b/packages/trace-viewer/src/ui/networkResourceDetails.tsx @@ -25,7 +25,8 @@ import { CopyToClipboardTextButton } from './copyToClipboard'; import { getAPIRequestCodeGen } from './codegen'; import type { Language } from '@isomorphic/locatorGenerators'; import { isJsonMimeType, isXmlMimeType } from '@isomorphic/mimeType'; -import { msToString, useAsyncMemo, useSetting } from '@web/uiUtils'; +import { useAsyncMemo, useSetting } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import type { Entry } from '@trace/har'; import { useTraceModel } from './traceModelContext'; import { Expandable } from '@web/components/expandable'; diff --git a/packages/trace-viewer/src/ui/networkTab.tsx b/packages/trace-viewer/src/ui/networkTab.tsx index af5bd13297eb1..4d02cc558e710 100644 --- a/packages/trace-viewer/src/ui/networkTab.tsx +++ b/packages/trace-viewer/src/ui/networkTab.tsx @@ -18,7 +18,7 @@ import * as React from 'react'; import type { Boundaries } from './geometry'; import './networkTab.css'; import { NetworkResourceDetails } from './networkResourceDetails'; -import { bytesToString, msToString } from '@web/uiUtils'; +import { bytesToString, msToString } from '@isomorphic/formatUtils'; import { PlaceholderPanel } from './placeholderPanel'; import { context, type ResourceEntry } from '@isomorphic/trace/traceModel'; import type { TraceModel } from '@isomorphic/trace/traceModel'; diff --git a/packages/trace-viewer/src/ui/timeline.tsx b/packages/trace-viewer/src/ui/timeline.tsx index 8da798d424ea4..855a6fa85fa27 100644 --- a/packages/trace-viewer/src/ui/timeline.tsx +++ b/packages/trace-viewer/src/ui/timeline.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { useSetting, msToString, useMeasure } from '@web/uiUtils'; +import { useSetting, useMeasure } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import { GlassPane } from '@web/shared/glassPane'; import * as React from 'react'; import type { Boundaries } from './geometry'; diff --git a/packages/trace-viewer/src/ui/uiModeTestListView.tsx b/packages/trace-viewer/src/ui/uiModeTestListView.tsx index fe76b8a744c03..2caacf3bb2692 100644 --- a/packages/trace-viewer/src/ui/uiModeTestListView.tsx +++ b/packages/trace-viewer/src/ui/uiModeTestListView.tsx @@ -22,7 +22,7 @@ import { ToolbarButton } from '@web/components/toolbarButton'; import type { TreeState } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView'; import '@web/third_party/vscode/codicon.css'; -import { msToString } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import type * as reporterTypes from 'playwright/types/testReporter'; import React from 'react'; import type { SourceLocation } from '@isomorphic/trace/traceModel'; diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 77a4a290b6c2d..e54b490d0256c 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -35,7 +35,8 @@ import { AnnotationsTab } from './annotationsTab'; import type { Boundaries } from './geometry'; import { InspectorTab } from './inspectorTab'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { useSetting, msToString, clsx, usePartitionedState, togglePartition } from '@web/uiUtils'; +import { useSetting, clsx, usePartitionedState, togglePartition } from '@web/uiUtils'; +import { msToString } from '@isomorphic/formatUtils'; import './workbench.css'; import { testStatusIcon, testStatusText } from './testUtils'; import type { UITestStatus } from './testUtils'; diff --git a/packages/web/src/DEPS.list b/packages/web/src/DEPS.list index 78980e8898bce..2903883d573fa 100644 --- a/packages/web/src/DEPS.list +++ b/packages/web/src/DEPS.list @@ -1,3 +1,4 @@ [*] +@isomorphic/** @playwright/experimental-ct-react third_party/** diff --git a/packages/web/src/uiUtils.ts b/packages/web/src/uiUtils.ts index 1c9818f3fbac2..48b68f91e0a8a 100644 --- a/packages/web/src/uiUtils.ts +++ b/packages/web/src/uiUtils.ts @@ -16,6 +16,7 @@ import React from 'react'; + // Recalculates the value when dependencies change. export function useAsyncMemo(fn: () => Promise, deps: React.DependencyList, initialValue: T, resetValue?: T) { const [value, setValue] = React.useState(initialValue); @@ -66,54 +67,6 @@ export function useMeasureForRef(ref?: React.RefObject(array: S[], object: T, comparator: (object: T, b: S) => number, left?: number, right?: number): number { let l = left || 0; let r = right !== undefined ? right : array.length; diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index cc996651d1543..fc9be4c718ffe 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -20,7 +20,7 @@ import url from 'url'; import { test as baseTest, expect as baseExpect, createImage } from './playwright-test-fixtures'; import type { HttpServer } from '../../packages/playwright-core/lib/server/utils/httpServer'; import { startHtmlReportServer } from '../../packages/playwright/lib/reporters/html'; -import { msToString } from '../../packages/web/src/uiUtils'; +import { msToString } from '../../packages/playwright-core/src/utils/isomorphic/formatUtils'; const { spawnAsync } = require('../../packages/playwright-core/lib/utils'); const test = baseTest.extend<{ showReport: (reportFolder?: string) => Promise }>({