diff --git a/assets/components/LogEventSource.spec.ts b/assets/components/LogEventSource.spec.ts index 8dd540f1..4eda4420 100644 --- a/assets/components/LogEventSource.spec.ts +++ b/assets/components/LogEventSource.spec.ts @@ -6,18 +6,10 @@ import LogEventSource from "./LogEventSource.vue"; import LogViewer from "./LogViewer.vue"; import { settings } from "../composables/settings"; import { useSearchFilter } from "@/composables/search"; -import { vi, describe, expect, beforeEach, test, beforeAll, afterAll } from "vitest"; -import { computed, Ref } from "vue"; +import { vi, describe, expect, beforeEach, test, beforeAll, afterAll, afterEach } from "vitest"; +import { computed, nextTick } from "vue"; import { createRouter, createWebHistory } from "vue-router"; -vi.mock("lodash.debounce", () => ({ - __esModule: true, - default: vi.fn((fn) => { - fn.cancel = () => {}; - return fn; - }), -})); - vi.mock("@/stores/config", () => ({ __esModule: true, default: { base: "" }, @@ -36,6 +28,13 @@ describe("", () => { observe: vi.fn(), disconnect: vi.fn(), })); + vi.useFakeTimers(); + vi.setSystemTime(1560336942459); + }); + + afterEach(() => { + vi.restoreAllMocks(); + vi.useRealTimers(); }); function createLogEventSource( @@ -103,38 +102,27 @@ describe("", () => { const wrapper = createLogEventSource(); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"This is a message.", "id":1}`, + data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`, }); + vi.runAllTimers(); + await nextTick(); + const [message, _] = wrapper.vm.messages; expect(message).toMatchSnapshot(); }); describe("render html correctly", () => { - const RealDate = Date; - beforeAll(() => { - // @ts-ignore - global.Date = class extends RealDate { - constructor(arg: any | number) { - super(arg); - if (arg) { - return new RealDate(arg); - } else { - return new RealDate(1560336936000); - } - } - }; - }); - afterAll(() => (global.Date = RealDate)); - test("should render messages", async () => { const wrapper = createLogEventSource(); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"This is a message.", "id":1}`, + data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`, }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); @@ -142,10 +130,12 @@ describe("", () => { const wrapper = createLogEventSource(); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: '{"ts":1560336942.459,"m":"\\u001b[30mblack\\u001b[37mwhite", "id":1}', + data: '{"ts":1560336942459,"m":"\\u001b[30mblack\\u001b[37mwhite", "id":1}', }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); @@ -153,10 +143,12 @@ describe("", () => { const wrapper = createLogEventSource(); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"foo bar", "id":1}`, + data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); @@ -164,10 +156,12 @@ describe("", () => { const wrapper = createLogEventSource({ hourStyle: "12" }); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"foo bar", "id":1}`, + data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); @@ -175,10 +169,12 @@ describe("", () => { const wrapper = createLogEventSource({ hourStyle: "24" }); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"foo bar", "id":1}`, + data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); @@ -186,13 +182,15 @@ describe("", () => { const wrapper = createLogEventSource({ searchFilter: "test" }); sources["/api/logs/stream?id=abc&lastEventId="].emitOpen(); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"foo bar", "id":1}`, + data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ - data: `{"ts":1560336942.459, "m":"test bar", "id":1}`, + data: `{"ts":1560336942459, "m":"test bar", "id":2}`, }); - await wrapper.vm.$nextTick(); + vi.runAllTimers(); + await nextTick(); + expect(wrapper.find("ul.events").html()).toMatchSnapshot(); }); }); diff --git a/assets/components/__snapshots__/LogEventSource.spec.ts.snap b/assets/components/__snapshots__/LogEventSource.spec.ts.snap index 1af0f3a2..de413971 100644 --- a/assets/components/__snapshots__/LogEventSource.spec.ts.snap +++ b/assets/components/__snapshots__/LogEventSource.spec.ts.snap @@ -122,37 +122,12 @@ exports[` > render html correctly > should render messages wit exports[` > render html correctly > should render messages with filter 1`] = ` "" diff --git a/assets/composables/eventsource.ts b/assets/composables/eventsource.ts index 9de7a239..292a0e1f 100644 --- a/assets/composables/eventsource.ts +++ b/assets/composables/eventsource.ts @@ -10,7 +10,7 @@ function parseMessage(data: string): LogEntry { const e = JSON.parse(data) as LogEvent; const id = e.id; - const date = new Date(e.ts * 1000); + const date = new Date(e.ts); return { id, date, message: e.m }; } diff --git a/docker/client.go b/docker/client.go index c24d42bf..d22bdeaa 100644 --- a/docker/client.go +++ b/docker/client.go @@ -151,7 +151,6 @@ func (d *dockerClient) ContainerStats(ctx context.Context, id string, stats chan memPercent = int64(float64(memUsage) / float64(v.MemoryStats.Limit) * 100) ) - if cpuPercent > 0 || memUsage > 0 { select { case <-ctx.Done(): @@ -174,8 +173,10 @@ func (d *dockerClient) ContainerLogs(ctx context.Context, id string, tailSize in log.WithField("id", id).WithField("since", since).Debug("streaming logs for container") if since != "" { - if sinceTime, err := time.Parse(time.RFC3339Nano, since); err == nil { - since = sinceTime.Add(time.Microsecond).Format(time.RFC3339Nano) + if millis, err := strconv.ParseInt(since, 10, 64); err == nil { + since = time.UnixMicro(millis).Add(time.Millisecond).Format(time.RFC3339Nano) + } else { + log.WithError(err).Debug("unable to parse since") } } diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 5afe597f..00000000 --- a/jest.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Config } from "@jest/types"; - -const config: Config.InitialOptions = { - preset: "ts-jest", - testEnvironment: "jsdom", - testPathIgnorePatterns: ["node_modules", "/integration/", "/e2e/"], - transform: { - "^.+\\.vue$": "@vue/vue3-jest", - }, - moduleNameMapper: { - "@/(.*)": ["/assets/$1"], - }, -}; - -export default config; diff --git a/web/__snapshots__/web.snapshot b/web/__snapshots__/web.snapshot index f730d5a8..9d96f12a 100644 --- a/web/__snapshots__/web.snapshot +++ b/web/__snapshots__/web.snapshot @@ -170,8 +170,8 @@ Connection: keep-alive Content-Type: text/event-stream X-Accel-Buffering: no -data: {"m":"INFO Testing logs...","ts":1589396137,"id":1469707724} -id: 1589396137 +data: {"m":"INFO Testing logs...","ts":1589396137772,"id":1469707724} +id: 1589396137772 event: container-stopped data: end of stream \ No newline at end of file diff --git a/web/logs.go b/web/logs.go index 9ee4a210..e5f70700 100644 --- a/web/logs.go +++ b/web/logs.go @@ -60,7 +60,7 @@ func logEventIterator(reader *bufio.Reader) func() (docker.LogEvent, error) { if index := strings.IndexAny(message, " "); index != -1 { logId := message[:index] if timestamp, err := time.Parse(time.RFC3339Nano, logId); err == nil { - logEvent.Timestamp = timestamp.Unix() + logEvent.Timestamp = timestamp.UnixMilli() message = strings.TrimSuffix(message[index+1:], "\n") logEvent.Message = message if strings.HasPrefix(message, "{") && strings.HasSuffix(message, "}") {