@skyhook-io/k8s-ui 1.5.10 → 1.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/dock/BottomDock.tsx +21 -10
- package/src/components/resources/get-pod-phase-display.test.ts +254 -0
- package/src/components/resources/renderers/AWSManagedMachinePoolRenderer.tsx +2 -4
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +3 -2
- package/src/components/resources/renderers/AzureManagedMachinePoolRenderer.tsx +3 -8
- package/src/components/resources/renderers/ChallengeRenderer.tsx +2 -1
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +2 -1
- package/src/components/resources/renderers/GatewayRenderer.tsx +5 -4
- package/src/components/resources/renderers/IngressClassRenderer.tsx +2 -1
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +2 -1
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +3 -2
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +2 -1
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -2
- package/src/components/resources/renderers/OrderRenderer.tsx +2 -1
- package/src/components/resources/renderers/PodRenderer.tsx +23 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +3 -2
- package/src/components/resources/renderers/RolloutRenderer.tsx +2 -1
- package/src/components/resources/renderers/SecretRenderer.tsx +15 -6
- package/src/components/resources/renderers/aws-capi-cells.tsx +2 -4
- package/src/components/resources/renderers/azure-capi-cells.tsx +3 -8
- package/src/components/resources/renderers/istio-cells.tsx +3 -2
- package/src/components/resources/renderers/karpenter-cells.tsx +1 -1
- package/src/components/resources/resource-utils-karpenter.ts +0 -6
- package/src/components/resources/resource-utils.ts +108 -0
- package/src/components/timeline/shared.tsx +5 -6
- package/src/components/ui/Tooltip.tsx +73 -40
- package/src/components/ui/tooltip-position.test.ts +132 -0
- package/src/components/ui/tooltip-position.ts +86 -0
- package/src/components/workload/WorkloadView.tsx +6 -6
- package/src/utils/badge-colors.ts +19 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/validators.test.ts +207 -0
- package/src/utils/validators.ts +178 -0
- package/src/utils/view-transition.test.ts +81 -0
- package/src/utils/view-transition.ts +62 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { isInvalidStateError, startViewTransitionSafe } from './view-transition'
|
|
3
|
+
|
|
4
|
+
// SKY-833 bug 49: rapid tab / route switches surfaced
|
|
5
|
+
// "InvalidStateError: Transition was aborted because of invalid state"
|
|
6
|
+
// as a console exception. The View Transitions API rejects the
|
|
7
|
+
// `finished` promise with that error whenever a new transition
|
|
8
|
+
// supersedes an in-flight one — which is the user's intent (they
|
|
9
|
+
// clicked again), not a bug. These tests pin the rejection-
|
|
10
|
+
// classification helper and the fallback path that's used when the
|
|
11
|
+
// API isn't available (Firefox, older Safari, jsdom).
|
|
12
|
+
|
|
13
|
+
describe('isInvalidStateError', () => {
|
|
14
|
+
it('returns true for objects whose name is "InvalidStateError"', () => {
|
|
15
|
+
expect(isInvalidStateError(new DOMException('boom', 'InvalidStateError'))).toBe(true)
|
|
16
|
+
expect(isInvalidStateError({ name: 'InvalidStateError', message: 'x' })).toBe(true)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('returns false for any other shape', () => {
|
|
20
|
+
expect(isInvalidStateError(new Error('boom'))).toBe(false)
|
|
21
|
+
expect(isInvalidStateError({ name: 'TypeError' })).toBe(false)
|
|
22
|
+
expect(isInvalidStateError({})).toBe(false)
|
|
23
|
+
expect(isInvalidStateError('InvalidStateError')).toBe(false)
|
|
24
|
+
expect(isInvalidStateError(null)).toBe(false)
|
|
25
|
+
expect(isInvalidStateError(undefined)).toBe(false)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('startViewTransitionSafe', () => {
|
|
30
|
+
it('falls back to invoking update directly when the API is missing', () => {
|
|
31
|
+
const originalDoc = (globalThis as { document?: unknown }).document
|
|
32
|
+
;(globalThis as { document: object }).document = {} // no startViewTransition
|
|
33
|
+
|
|
34
|
+
const update = vi.fn()
|
|
35
|
+
startViewTransitionSafe(update)
|
|
36
|
+
expect(update).toHaveBeenCalledTimes(1)
|
|
37
|
+
|
|
38
|
+
;(globalThis as { document?: unknown }).document = originalDoc
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('uses the API when available and swallows InvalidStateError on the finished promise', async () => {
|
|
42
|
+
const originalDoc = (globalThis as { document?: unknown }).document
|
|
43
|
+
|
|
44
|
+
const update = vi.fn()
|
|
45
|
+
const startSpy = vi.fn((cb: () => void) => {
|
|
46
|
+
void cb // the real API invokes this; test doesn't need to
|
|
47
|
+
return {
|
|
48
|
+
finished: Promise.reject(new DOMException('superseded', 'InvalidStateError')),
|
|
49
|
+
ready: Promise.resolve(),
|
|
50
|
+
updateCallbackDone: Promise.resolve(),
|
|
51
|
+
skipTransition: () => {},
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
;(globalThis as { document: object }).document = { startViewTransition: startSpy }
|
|
55
|
+
|
|
56
|
+
startViewTransitionSafe(update)
|
|
57
|
+
expect(startSpy).toHaveBeenCalledTimes(1)
|
|
58
|
+
// Argument is the update callback (the API itself invokes it).
|
|
59
|
+
expect(startSpy).toHaveBeenCalledWith(update)
|
|
60
|
+
|
|
61
|
+
// The unhandled rejection would surface here if we hadn't caught
|
|
62
|
+
// it. Awaiting a microtask yield is enough to flush the promise
|
|
63
|
+
// chain inside startViewTransitionSafe.
|
|
64
|
+
await Promise.resolve()
|
|
65
|
+
await Promise.resolve()
|
|
66
|
+
|
|
67
|
+
;(globalThis as { document?: unknown }).document = originalDoc
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('only swallows InvalidStateError (verified via the classifier)', () => {
|
|
71
|
+
// Rather than triggering an actual unhandled rejection (which
|
|
72
|
+
// vitest treats as a test failure), pin the contract that
|
|
73
|
+
// startViewTransitionSafe relies on: InvalidStateError is the
|
|
74
|
+
// single name we suppress. Anything else returns false from the
|
|
75
|
+
// classifier and therefore propagates through the promise chain.
|
|
76
|
+
expect(isInvalidStateError(new DOMException('x', 'InvalidStateError'))).toBe(true)
|
|
77
|
+
expect(isInvalidStateError(new Error('something else broke'))).toBe(false)
|
|
78
|
+
expect(isInvalidStateError(new DOMException('x', 'AbortError'))).toBe(false)
|
|
79
|
+
expect(isInvalidStateError(new TypeError('y'))).toBe(false)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper around `document.startViewTransition` that:
|
|
3
|
+
*
|
|
4
|
+
* 1. Falls back to a synchronous call to `update` when the View
|
|
5
|
+
* Transitions API isn't available (older Safari, Firefox without
|
|
6
|
+
* the flag, jsdom in tests).
|
|
7
|
+
* 2. Swallows the `InvalidStateError` that the API rejects with
|
|
8
|
+
* whenever a new transition starts while a previous one is still
|
|
9
|
+
* pending. This is a normal symptom of impatient users (clicking
|
|
10
|
+
* tabs faster than 200ms) — the new transition has already
|
|
11
|
+
* replaced the in-flight one, so there is nothing for the caller
|
|
12
|
+
* to do. SKY-833 bug 49 surfaced this as a console exception
|
|
13
|
+
* stack on rapid Secrets / drawer-tab navigation.
|
|
14
|
+
*
|
|
15
|
+
* Other rejection types (programming errors, unsupported configs)
|
|
16
|
+
* are re-thrown so we don't accidentally hide real bugs.
|
|
17
|
+
*/
|
|
18
|
+
export function startViewTransitionSafe(update: () => void): void {
|
|
19
|
+
// Some lib.dom.d.ts versions don't yet expose `startViewTransition`
|
|
20
|
+
// on Document. Narrow once via a typed cast.
|
|
21
|
+
const doc = document as Document & {
|
|
22
|
+
startViewTransition?: (cb: () => void) => {
|
|
23
|
+
finished: Promise<void>
|
|
24
|
+
ready: Promise<void>
|
|
25
|
+
updateCallbackDone: Promise<void>
|
|
26
|
+
skipTransition: () => void
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof doc.startViewTransition !== 'function') {
|
|
31
|
+
update()
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const transition = doc.startViewTransition(update)
|
|
36
|
+
|
|
37
|
+
// The `finished` promise rejects with InvalidStateError when a new
|
|
38
|
+
// transition supersedes this one. That's not an error condition for
|
|
39
|
+
// the user — they got what they wanted (the new state). Swallow it
|
|
40
|
+
// so it doesn't become a noisy console exception.
|
|
41
|
+
transition.finished.catch((err: unknown) => {
|
|
42
|
+
if (isInvalidStateError(err)) return
|
|
43
|
+
// Anything else really is unexpected — let it surface.
|
|
44
|
+
throw err
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Predicate that classifies the rejection thrown by the View
|
|
50
|
+
* Transitions API when a new transition supersedes an in-flight one.
|
|
51
|
+
* Used by `startViewTransitionSafe` to know what to swallow, and
|
|
52
|
+
* exported so tests (and any future call site that wraps the API
|
|
53
|
+
* differently) can pin the exact name we treat as expected.
|
|
54
|
+
*/
|
|
55
|
+
export function isInvalidStateError(err: unknown): boolean {
|
|
56
|
+
return (
|
|
57
|
+
!!err &&
|
|
58
|
+
typeof err === 'object' &&
|
|
59
|
+
'name' in err &&
|
|
60
|
+
(err as { name: unknown }).name === 'InvalidStateError'
|
|
61
|
+
)
|
|
62
|
+
}
|