Skip to content
Closed
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.

## Unreleased

### The desktop setup's question box waits for a typed character to be confirmed

Japanese, Chinese and Korean are typed through an input method, where Enter confirms the character
being built. On the last setup screen that Enter also sent the question, with its last character
still unconfirmed. The box now waits for the character, the way the chat composer already does, and
an ordinary Enter still asks.

### A malformed page size is refused instead of silently coerced

`GET /channels` and `GET /api/admin/people` read `?limit=` with `Number.parseInt`, which
Expand Down
55 changes: 54 additions & 1 deletion desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { afterAll, afterEach, beforeAll, expect, mock, test } from "bun:test";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import { act, cleanup, render, waitFor } from "@testing-library/react";
import {
act,
cleanup,
fireEvent,
render,
waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { StrictMode } from "react";

Expand Down Expand Up @@ -131,6 +137,53 @@ test("setup records only model categories and reaches Ask when telemetry is unav
expect(view.queryByText(/Nothing here leaves this computer/)).toBeNull();
});

/**
* Setup finished and the question box on screen, focused as a person's typing would be. Call
* `useCompatibleEndpointSetup` first, as every test here does.
*/
async function reachAsk() {
const previous = invokeHandler;
invokeHandler = async (command, args) => {
if (command === "record_setup_event") return null;
if (command === "ask_the_bot") return "391";
return previous(command, args);
};
const view = await enterCompatibleEndpoint("https://models.example/v1");
await userEvent.click(view.getByRole("button", { name: "Continue" }));
await userEvent.click(view.getByRole("button", { name: "Start OpenBot" }));
const question = await view.findByLabelText("Your question");
// Keys go to the input that has focus, so the field is entered first.
await userEvent.click(question);
const asked = () =>
invokeCalls.filter((call) => call.command === "ask_the_bot");
return { question, asked };
}

test("the Enter that finishes a composed character does not ask the Bot", async () => {
useCompatibleEndpointSetup({});
const { question, asked } = await reachAsk();

// Japanese, Chinese and Korean are typed through an input method, and Enter is how a character is
// confirmed. That Enter is still a keydown: Chromium marks it `isComposing`, and WebKit — the
// macOS webview — sends it after `compositionend` with the key code 229 instead.
await act(async () => {
fireEvent.keyDown(question, { key: "Enter", isComposing: true });
fireEvent.keyDown(question, { key: "Enter", keyCode: 229 });
});
expect(asked()).toEqual([]);
});

test("an ordinary Enter in the question box asks the Bot, once", async () => {
// Unchanged by the test above, and pinned so it stays that way.
useCompatibleEndpointSetup({});
const { question, asked } = await reachAsk();

await act(async () => {
fireEvent.keyDown(question, { key: "Enter", keyCode: 13 });
});
await waitFor(() => expect(asked()).toHaveLength(1));
});

type StartStackPayload = {
root?: unknown;
apiKey?: unknown;
Expand Down
15 changes: 14 additions & 1 deletion desktop/src/Ask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,20 @@ export function Ask({
}}
disabled={asking}
onKeyDown={(event) => {
if (event.key === "Enter" && !asking) {
/*
* NOT THE ENTER THAT FINISHES A COMPOSED CHARACTER. Japanese, Chinese and Korean are
* typed through an input method, and Enter is how the character being built is
* confirmed. That press is still a keydown: Chromium marks it `isComposing`, and WebKit,
* which the macOS webview is, sends it after `compositionend` with the key code 229. Read
* as a plain Enter, it sent the question with the last character still unconfirmed. The
* chat composer's own Enter already skips it; this one did not.
*/
if (
event.key === "Enter" &&
!asking &&
!event.nativeEvent.isComposing &&
event.keyCode !== 229
) {
ask();
}
}}
Expand Down