Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 86 additions & 7 deletions web/src/CopyLogs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,29 @@ import { act, render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import React from "react"
import CopyLogs, { copyLogs } from "./CopyLogs"
import { logLinesToString, stripAnsiCodes } from "./logs"
import {
filterLogLinesForDisplay,
logLinesToString,
stripAnsiCodes,
} from "./logs"
import {
createFilterTermState,
EMPTY_FILTER_TERM,
FilterLevel,
FilterSet,
FilterSource,
} from "./logfilters"
import LogStore, { LogStoreProvider } from "./LogStore"
import { StarredResourceMemoryProvider } from "./StarredResourcesContext"
import { appendLinesForManifestAndSpan } from "./testlogs"
import { ResourceName } from "./types"

const DEFAULT_FILTER_SET: FilterSet = {
source: FilterSource.all,
level: FilterLevel.all,
term: EMPTY_FILTER_TERM,
}

describe("CopyLogs", () => {
let writeTextMock: jest.Mock

Expand Down Expand Up @@ -53,7 +71,10 @@ describe("CopyLogs", () => {
const logStore = createPopulatedLogStore()
render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={ResourceName.all} />
<CopyLogs
resourceName={ResourceName.all}
filterSet={DEFAULT_FILTER_SET}
/>
</LogStoreProvider>
)

Expand All @@ -69,7 +90,7 @@ describe("CopyLogs", () => {
const logStore = createPopulatedLogStore()
render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={"vigoda"} />
<CopyLogs resourceName={"vigoda"} filterSet={DEFAULT_FILTER_SET} />
</LogStoreProvider>
)

Expand All @@ -87,7 +108,10 @@ describe("CopyLogs", () => {

render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={ResourceName.all} />
<CopyLogs
resourceName={ResourceName.all}
filterSet={DEFAULT_FILTER_SET}
/>
</LogStoreProvider>
)

Expand All @@ -103,7 +127,10 @@ describe("CopyLogs", () => {
const logStore = createPopulatedLogStore()
render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={ResourceName.all} />
<CopyLogs
resourceName={ResourceName.all}
filterSet={DEFAULT_FILTER_SET}
/>
</LogStoreProvider>
)

Expand All @@ -119,7 +146,10 @@ describe("CopyLogs", () => {
const logStore = createPopulatedLogStore()
render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={ResourceName.all} />
<CopyLogs
resourceName={ResourceName.all}
filterSet={DEFAULT_FILTER_SET}
/>
</LogStoreProvider>
)

Expand All @@ -140,9 +170,58 @@ describe("CopyLogs", () => {

it("returns the number of lines copied", () => {
const logStore = createPopulatedLogStore()
const count = copyLogs(logStore, ResourceName.all)
const count = copyLogs(logStore, ResourceName.all, DEFAULT_FILTER_SET)
expect(count).toBe(logStore.allLog().length)
})

it("copies only the currently filtered log lines", async () => {
const logStore = createPopulatedLogStore()
const filterSet: FilterSet = {
...DEFAULT_FILTER_SET,
term: createFilterTermState("runtime"),
}

render(
<LogStoreProvider value={logStore}>
<CopyLogs resourceName={ResourceName.all} filterSet={filterSet} />
</LogStoreProvider>
)

await act(async () => {
userEvent.click(screen.getByText("Copy"))
})

const expectedText = logLinesToString(
filterLogLinesForDisplay(logStore.allLog(), filterSet),
false
)
expect(writeTextMock).toHaveBeenCalledWith(expectedText)
})

it("copies only starred resource logs in starred view", async () => {
const logStore = createPopulatedLogStore()

render(
<StarredResourceMemoryProvider initialValueForTesting={["vigoda"]}>
<LogStoreProvider value={logStore}>
<CopyLogs
resourceName={ResourceName.starred}
filterSet={DEFAULT_FILTER_SET}
/>
</LogStoreProvider>
</StarredResourceMemoryProvider>
)

await act(async () => {
userEvent.click(screen.getByText("Copy"))
})

const expectedText = logLinesToString(
logStore.starredLogPatchSet(["vigoda"], 0).lines,
true
)
expect(writeTextMock).toHaveBeenCalledWith(expectedText)
})
})

describe("stripAnsiCodes", () => {
Expand Down
45 changes: 37 additions & 8 deletions web/src/CopyLogs.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { useState } from "react"
import styled from "styled-components"
import { InstrumentedButton } from "./instrumentedComponents"
import { FilterSet } from "./logfilters"
import LogStore, { useLogStore } from "./LogStore"
import { logLinesToString } from "./logs"
import { filterLogLinesForDisplay, logLinesToString } from "./logs"
import { useStarredResources } from "./StarredResourcesContext"
import {
AnimDuration,
Color,
Expand All @@ -25,25 +27,52 @@ const CopyLogsButton = styled(InstrumentedButton)`

export interface CopyLogsProps {
resourceName: string
filterSet: FilterSet
}

export const copyLogs = (logStore: LogStore, resourceName: string): number => {
function logLinesForResource(
logStore: LogStore,
resourceName: string,
starredResources: string[]
) {
const all = resourceName === ResourceName.all
const lines = all ? logStore.allLog() : logStore.manifestLog(resourceName)
const text = logLinesToString(lines, !all)
if (all) {
return logStore.allLog()
}
if (resourceName === ResourceName.starred) {
return logStore.starredLogPatchSet(starredResources, 0).lines
}
return logStore.manifestLog(resourceName)
}

export const copyLogs = (
logStore: LogStore,
resourceName: string,
filterSet: FilterSet,
starredResources: string[] = []
): number => {
const all = resourceName === ResourceName.all
const lines = logLinesForResource(logStore, resourceName, starredResources)
const visibleLines = filterLogLinesForDisplay(lines, filterSet)
const text = logLinesToString(visibleLines, !all)
navigator.clipboard.writeText(text)
return lines.length
return visibleLines.length
}

const CopyLogs: React.FC<CopyLogsProps> = ({ resourceName }) => {
const CopyLogs: React.FC<CopyLogsProps> = ({ resourceName, filterSet }) => {
const logStore = useLogStore()
const all = resourceName == ResourceName.all
const { starredResources } = useStarredResources()
const label = "Copy"
const [tooltipOpen, setTooltipOpen] = useState(false)
const [tooltipText, setTooltipText] = useState("")

const handleClick = () => {
const lineCount = copyLogs(logStore, resourceName)
const lineCount = copyLogs(
logStore,
resourceName,
filterSet,
starredResources
)
setTooltipText(
lineCount === 1 ? "Copied 1 line" : `Copied ${lineCount} lines`
)
Expand Down
5 changes: 4 additions & 1 deletion web/src/LogActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styled from "styled-components"
import ClearLogs from "./ClearLogs"
import CopyLogs from "./CopyLogs"
import { InstrumentedButton } from "./instrumentedComponents"
import { FilterSet } from "./logfilters"
import {
AnimDuration,
Color,
Expand Down Expand Up @@ -116,19 +117,21 @@ export const LogsFontSize: React.FC = () => {
export interface LogActionsProps {
resourceName: string
isSnapshot: boolean
filterSet: FilterSet
}

const LogActions: React.FC<LogActionsProps> = ({
resourceName,
isSnapshot,
filterSet,
}) => {
return (
<LogActionsGroup>
<LogsFontSize />
{isSnapshot || (
<>
<LogActionsDivider aria-hidden={true}>|</LogActionsDivider>
<CopyLogs resourceName={resourceName} />
<CopyLogs resourceName={resourceName} filterSet={filterSet} />
<LogActionsDivider aria-hidden={true}>|</LogActionsDivider>
<ClearLogs resourceName={resourceName} />
</>
Expand Down
1 change: 1 addition & 0 deletions web/src/OverviewActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ export default function OverviewActionBar(props: OverviewActionBarProps) {
key="logActions"
resourceName={resourceName}
isSnapshot={isSnapshot}
filterSet={filterSet}
/>
)
}
Expand Down
Loading