mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: update cSpell and fix isNotificationTextPrefixed (#4076)
* chore: update cSpell words * chore: add ignorePaths for cSpell * fix: update isNotificationTextPrefixed This removes an eslint-disable rule and adds two new tests to ensure isNotificationTextPrefixed is working as expected. * fix(e2e): remove filter in workspacesPage
This commit is contained in:
Vendored
+9
@@ -8,6 +8,8 @@
|
||||
"circbuf",
|
||||
"cliflag",
|
||||
"cliui",
|
||||
"codecov",
|
||||
"Codespaces",
|
||||
"coderd",
|
||||
"coderdtest",
|
||||
"codersdk",
|
||||
@@ -80,6 +82,7 @@
|
||||
"ptty",
|
||||
"ptys",
|
||||
"ptytest",
|
||||
"quickstart",
|
||||
"reconfig",
|
||||
"retrier",
|
||||
"rpty",
|
||||
@@ -117,10 +120,12 @@
|
||||
"tstun",
|
||||
"turnconn",
|
||||
"typegen",
|
||||
"typesafe",
|
||||
"unconvert",
|
||||
"Untar",
|
||||
"Userspace",
|
||||
"VMID",
|
||||
"walkthrough",
|
||||
"weblinks",
|
||||
"webrtc",
|
||||
"wgcfg",
|
||||
@@ -140,6 +145,10 @@
|
||||
"xstate",
|
||||
"yamux"
|
||||
],
|
||||
"cSpell.ignorePaths": [
|
||||
"site/package.json",
|
||||
".vscode/settings.json"
|
||||
],
|
||||
"emeraldwalk.runonsave": {
|
||||
"commands": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { test } from "@playwright/test"
|
||||
import { email, password } from "../constants"
|
||||
import { SignInPage, WorkspacesPage } from "../pom"
|
||||
import { waitForClientSideNavigation } from "./../util"
|
||||
import { SignInPage } from "../pom"
|
||||
|
||||
test("Login takes user to /workspaces", async ({ baseURL, page }) => {
|
||||
await page.goto(baseURL + "/", { waitUntil: "networkidle" })
|
||||
@@ -10,8 +9,5 @@ test("Login takes user to /workspaces", async ({ baseURL, page }) => {
|
||||
const signInPage = new SignInPage(baseURL, page)
|
||||
await signInPage.submitBuiltInAuthentication(email, password)
|
||||
|
||||
const workspacesPage = new WorkspacesPage(baseURL, page, "?filter=owner%3Ame")
|
||||
await waitForClientSideNavigation(page, { to: workspacesPage.url })
|
||||
|
||||
await page.waitForSelector("text=Workspaces")
|
||||
})
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Page } from "@playwright/test"
|
||||
|
||||
/**
|
||||
* `timeout(x)` is a helper function to create a promise that resolves after `x` milliseconds.
|
||||
*
|
||||
* @param timeoutInMilliseconds Time to wait for promise to resolve
|
||||
* @returns `Promise`
|
||||
*/
|
||||
export const timeout = (timeoutInMilliseconds: number): Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, timeoutInMilliseconds)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* `waitFor(f, timeout?)` waits for a predicate to return `true`, running it periodically until it returns `true`.
|
||||
*
|
||||
* If `f` never returns `true`, the function will simply return. In other words, the burden is on the consumer
|
||||
* to check that the predicate is passing (`waitFor` does no validation).
|
||||
*
|
||||
* @param f A predicate that returns a `Promise<boolean>`
|
||||
* @param timeToWaitInMilliseconds The total time to wait for the condition to be `true`.
|
||||
* @returns
|
||||
*/
|
||||
export const waitFor = async (
|
||||
f: () => Promise<boolean>,
|
||||
timeToWaitInMilliseconds = 30000,
|
||||
): Promise<void> => {
|
||||
let elapsedTime = 0
|
||||
const timeToWaitPerIteration = 1000
|
||||
|
||||
while (elapsedTime < timeToWaitInMilliseconds) {
|
||||
const condition = await f()
|
||||
|
||||
if (condition) {
|
||||
return
|
||||
}
|
||||
|
||||
await timeout(timeToWaitPerIteration)
|
||||
elapsedTime += timeToWaitPerIteration
|
||||
}
|
||||
}
|
||||
|
||||
interface WaitForClientSideNavigationOpts {
|
||||
/**
|
||||
* from is the page before navigation (the 'current' page)
|
||||
*/
|
||||
from?: string
|
||||
/**
|
||||
* to is the page after navigation (the 'next' page)
|
||||
*/
|
||||
to?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* waitForClientSideNavigation waits for the url to change from opts.from to
|
||||
* opts.to (if specified), as well as a network idle load state. This enhances
|
||||
* a native playwright check for navigation or loadstate.
|
||||
*
|
||||
* @remark This is necessary in a client-side SPA world since playwright
|
||||
* waitForNavigation waits for load events on the DOM (ex: after a page load
|
||||
* from the server).
|
||||
*/
|
||||
export const waitForClientSideNavigation = async (
|
||||
page: Page,
|
||||
opts: WaitForClientSideNavigationOpts,
|
||||
): Promise<void> => {
|
||||
console.info(`--- waitForClientSideNavigation: start`)
|
||||
|
||||
await Promise.all([
|
||||
waitFor(() => {
|
||||
const conditions: boolean[] = []
|
||||
|
||||
if (opts.from) {
|
||||
conditions.push(page.url() !== opts.from)
|
||||
}
|
||||
|
||||
if (opts.to) {
|
||||
conditions.push(page.url() === opts.to)
|
||||
}
|
||||
|
||||
const unmetConditions = conditions.filter((condition) => !condition)
|
||||
console.info(`--- waitForClientSideNavigation: ${unmetConditions.length} conditions not met`)
|
||||
|
||||
return Promise.resolve(unmetConditions.length === 0)
|
||||
}),
|
||||
page.waitForLoadState("networkidle"),
|
||||
])
|
||||
|
||||
console.info(`--- waitForClientSideNavigation: done`)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
isNotificationTextPrefixed,
|
||||
MsgType,
|
||||
NotificationMsg,
|
||||
NotificationTextPrefixed,
|
||||
SnackbarEventType,
|
||||
} from "./utils"
|
||||
|
||||
@@ -17,6 +18,29 @@ describe("Snackbar", () => {
|
||||
// When
|
||||
const isTextPrefixed = isNotificationTextPrefixed(msg)
|
||||
|
||||
// Then
|
||||
expect(isTextPrefixed).toBe(false)
|
||||
})
|
||||
it("returns true if prefixed", () => {
|
||||
// Given
|
||||
const msg: NotificationTextPrefixed = {
|
||||
prefix: "warning",
|
||||
text: "careful with this workspace",
|
||||
}
|
||||
|
||||
// When
|
||||
const isTextPrefixed = isNotificationTextPrefixed(msg)
|
||||
|
||||
// Then
|
||||
expect(isTextPrefixed).toBe(true)
|
||||
})
|
||||
it("returns false if not prefixed", () => {
|
||||
// Given
|
||||
const msg = "plain ol' message"
|
||||
|
||||
// When
|
||||
const isTextPrefixed = isNotificationTextPrefixed(msg)
|
||||
|
||||
// Then
|
||||
expect(isTextPrefixed).toBe(false)
|
||||
})
|
||||
|
||||
@@ -27,8 +27,10 @@ export const isNotificationText = (msg: AdditionalMessage): msg is string => {
|
||||
export const isNotificationTextPrefixed = (
|
||||
msg: AdditionalMessage | null,
|
||||
): msg is NotificationTextPrefixed => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return typeof (msg as NotificationTextPrefixed)?.prefix !== "undefined"
|
||||
if (msg) {
|
||||
return typeof msg !== "string" && Object.prototype.hasOwnProperty.call(msg, "prefix")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const isNotificationList = (msg: AdditionalMessage): msg is string[] => {
|
||||
|
||||
Reference in New Issue
Block a user