BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 114 lines 3.8 kB view raw
1import { fireEvent, render, screen } from "@solidjs/testing-library"; 2import { createSignal } from "solid-js"; 3import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; 4import { LoginPanel } from "../LoginPanel"; 5 6const invokeMock = vi.hoisted(() => vi.fn()); 7 8vi.mock("@tauri-apps/api/core", () => ({ invoke: invokeMock })); 9 10function renderPanel(overrides: Partial<Parameters<typeof LoginPanel>[0]> = {}) { 11 const defaults = { value: "", pending: false, shakeCount: 0, onInput: vi.fn(), onSubmit: vi.fn() }; 12 13 return render(() => <LoginPanel {...{ ...defaults, ...overrides }} />); 14} 15 16function renderInteractivePanel() { 17 const onSubmit = vi.fn(); 18 19 return { 20 onSubmit, 21 ...render(() => { 22 const [value, setValue] = createSignal(""); 23 return <LoginPanel value={value()} pending={false} shakeCount={0} onInput={setValue} onSubmit={onSubmit} />; 24 }), 25 }; 26} 27 28describe("LoginPanel", () => { 29 beforeEach(() => { 30 vi.useFakeTimers(); 31 invokeMock.mockReset(); 32 invokeMock.mockResolvedValue([]); 33 }); 34 35 afterEach(() => { 36 vi.useRealTimers(); 37 }); 38 39 it("renders branded header with Lazurite logo", () => { 40 renderPanel(); 41 42 expect(screen.getByText("Lazurite")).toBeInTheDocument(); 43 expect(screen.getByText("Powered by Bluesky")).toBeInTheDocument(); 44 expect(screen.getByText(/sign in with your/i)).toBeInTheDocument(); 45 expect(screen.getByRole("link", { name: "Internet Handle" })).toBeInTheDocument(); 46 47 const svg = document.querySelector("svg"); 48 expect(svg).toBeInTheDocument(); 49 expect(svg).toHaveAttribute("fill", "currentColor"); 50 }); 51 52 it("uses solid primary background on submit button (no gradient)", () => { 53 renderPanel(); 54 55 const button = screen.getByRole("button", { name: /continue/i }); 56 expect(button.className).toContain("bg-primary"); 57 expect(button.className).not.toContain("gradient"); 58 }); 59 60 it("uses rounded-xl on input (not rounded-full)", () => { 61 renderPanel(); 62 63 const input = screen.getByPlaceholderText("alice.bsky.social"); 64 expect(input.className).toContain("rounded-xl"); 65 expect(input.className).not.toContain("rounded-full"); 66 }); 67 68 it("shows loading state when pending", () => { 69 renderPanel({ pending: true }); 70 71 expect(screen.getByText("Opening sign-in...")).toBeInTheDocument(); 72 expect(screen.getByRole("button")).toBeDisabled(); 73 }); 74 75 it("requests autocomplete suggestions for handle-like input", async () => { 76 invokeMock.mockResolvedValue([{ 77 did: "did:plc:alice", 78 handle: "alice.bsky.social", 79 displayName: "Alice Example", 80 avatar: null, 81 }]); 82 83 renderInteractivePanel(); 84 85 const input = screen.getByPlaceholderText("alice.bsky.social"); 86 input.focus(); 87 fireEvent.input(input, { target: { value: "ali" } }); 88 await vi.advanceTimersByTimeAsync(200); 89 90 expect(invokeMock).toHaveBeenCalledWith("search_login_suggestions", { query: "ali" }); 91 expect(await screen.findByText("Alice Example")).toBeInTheDocument(); 92 expect(screen.getByText("@alice.bsky.social")).toBeInTheDocument(); 93 }); 94 95 it("applies the highlighted suggestion on enter instead of submitting immediately", async () => { 96 const { onSubmit } = renderInteractivePanel(); 97 invokeMock.mockResolvedValue([{ 98 did: "did:plc:alice", 99 handle: "alice.bsky.social", 100 displayName: "Alice Example", 101 avatar: null, 102 }]); 103 104 const input = screen.getByPlaceholderText("alice.bsky.social"); 105 input.focus(); 106 fireEvent.input(input, { target: { value: "ali" } }); 107 await vi.advanceTimersByTimeAsync(200); 108 109 fireEvent.keyDown(input, { key: "Enter" }); 110 111 expect(screen.getByDisplayValue("alice.bsky.social")).toBeInTheDocument(); 112 expect(onSubmit).not.toHaveBeenCalled(); 113 }); 114});