Skip to content

Commit c49ebbf

Browse files
committed
test: simulate onboarding sign-in in Midscene
1 parent 5af0340 commit c49ebbf

4 files changed

Lines changed: 88 additions & 9 deletions

File tree

‎tests/midscene/README.md‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,14 @@ The manual experimental workflow now continues from that installed base image:
4545
Omarchy/Hyprland guest session.
4646

4747
The ordinary Ubuntu stage maps the real GTK onboarding window inside the
48-
headless Midscene desktop. Its synthetic fixture performs no login, recording,
49-
network request, paste, or user-settings read. Midscene visually navigates to
50-
the shortcut page, runs the fake endpoint check, verifies its visible feedback,
51-
continues to the voice page, checks that navigation reset the scroll position,
52-
and completes setup. The HTML replay is uploaded for every run.
48+
headless Midscene desktop. Its synthetic fixture starts signed out, opens an
49+
explicitly labelled CI-only sign-in window, and simulates a successful return
50+
without a network request or real credentials. It also performs no recording,
51+
paste, or user-settings read. Midscene verifies the automatic transition to the
52+
microphone step, visually navigates to the shortcut page, runs the fake endpoint
53+
check, verifies its visible feedback, continues to the voice page, checks that
54+
navigation reset the scroll position, and completes setup. The HTML replay is
55+
uploaded for every run.
5356

5457
The AI stage needs a multimodal model credential. Its `MIDSCENE_MODEL_*`
5558
configuration is stored only as GitHub Actions Secrets; tests contain only

‎tests/midscene/e2e/omarchy-onboarding.test.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,19 @@ describe.skipIf(process.env.OMARCHY_E2E !== 'true')(
6565

6666
it('runs the onboarding flow in the real Omarchy desktop session', async () => {
6767
await agent.aiAct(
68-
'Verify the Omarchy desktop visibly contains the Doubao Say window on the Microphone step with a Next button in its fixed top navigation.',
68+
'Verify the Omarchy desktop visibly contains the Doubao Say window on the Sign in step, says the user is not signed in, and shows an Open Doubao sign-in button. Do not click anything.',
69+
);
70+
await agent.aiAct(
71+
'In the Doubao Say window, click Open Doubao sign-in exactly once, then stop immediately.',
72+
);
73+
await agent.aiAct(
74+
'Verify a Synthetic Doubao sign-in window is visible and explicitly says it is CI-only, makes no network request, and uses no real credentials. Do not click anything.',
75+
);
76+
await agent.aiAct(
77+
'In the Synthetic Doubao sign-in window, click Simulate successful sign-in exactly once, then stop immediately.',
78+
);
79+
await agent.aiAct(
80+
'Verify the synthetic sign-in window closed and the Doubao Say window automatically advanced to the Microphone step with a Next button in its fixed top navigation.',
6981
);
7082
await agent.aiAct(
7183
'Click Next in the Doubao Say fixed top navigation exactly once, then stop immediately. ' +

‎tests/midscene/e2e/onboarding.test.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,19 @@ describe('Doubao Say onboarding', () => {
9494

9595
it('navigates, reports endpoint feedback, resets scroll and completes setup', async () => {
9696
await agent.aiAct(
97-
'Verify the visible Doubao Say window is on the Microphone step and has a Next button in the fixed top navigation.',
97+
'Verify the visible Doubao Say window starts on the Sign in step, says the user is not signed in, and shows an Open Doubao sign-in button. Do not click anything.',
98+
);
99+
await agent.aiAct(
100+
'Click Open Doubao sign-in exactly once, then stop immediately.',
101+
);
102+
await agent.aiAct(
103+
'Verify a Synthetic Doubao sign-in window is visible and explicitly says it is CI-only, makes no network request, and uses no real credentials. Do not click anything.',
104+
);
105+
await agent.aiAct(
106+
'In the Synthetic Doubao sign-in window, click Simulate successful sign-in exactly once, then stop immediately.',
107+
);
108+
await agent.aiAct(
109+
'Verify the synthetic sign-in window closed and the visible Doubao Say window automatically advanced to the Microphone step with a Next button in the fixed top navigation.',
98110
);
99111

100112
await agent.aiAct(

‎tests/midscene/gtk_fixture.py‎

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def main():
1818
Gtk.init()
1919
set_language("en")
2020
state = AppState()
21-
state.login_status = LoginStatus.LOGGED_IN
21+
state.login_status = LoginStatus.NOT_LOGGED_IN
2222
settings = Settings(
2323
polish_enabled=True,
2424
polish_base_url="https://example.invalid/v1",
@@ -36,6 +36,58 @@ def main():
3636
}
3737
holder = {}
3838

39+
def show_synthetic_login():
40+
existing = holder.get("login")
41+
if existing:
42+
existing.present()
43+
return
44+
45+
login = Gtk.Window(title="Doubao Say — Synthetic sign-in")
46+
login.set_default_size(520, 280)
47+
login.set_resizable(False)
48+
login.set_modal(True)
49+
login.set_transient_for(holder["control"].window)
50+
51+
content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=18)
52+
for side in ("start", "end", "top", "bottom"):
53+
getattr(content, "set_margin_" + side)(32)
54+
login.set_child(content)
55+
56+
heading = Gtk.Label(
57+
label="Synthetic Doubao sign-in",
58+
xalign=0,
59+
)
60+
heading.add_css_class("title-2")
61+
content.append(heading)
62+
content.append(Gtk.Label(
63+
label=(
64+
"CI-only window. It makes no network request and uses no real "
65+
"credentials."
66+
),
67+
xalign=0,
68+
wrap=True,
69+
))
70+
71+
success = Gtk.Button(label="Simulate successful sign-in")
72+
success.add_css_class("suggested-action")
73+
content.append(success)
74+
75+
def signed_in(*_args):
76+
holder["login"] = None
77+
login.destroy()
78+
state.login_status = LoginStatus.LOGGED_IN
79+
holder["control"].advance_after_login()
80+
81+
success.connect("clicked", signed_in)
82+
83+
def closed(*_args):
84+
holder["login"] = None
85+
return False
86+
87+
login.connect("close-request", closed)
88+
holder["login"] = login
89+
login.present()
90+
3991
def test_endpoint(_settings, _key, done):
4092
def finish():
4193
done("Synthetic endpoint response", None)
@@ -66,7 +118,7 @@ def complete_setup():
66118
)
67119
control = ControlWindow(
68120
state,
69-
on_login_clicked=lambda: None,
121+
on_login_clicked=show_synthetic_login,
70122
on_quit_clicked=lambda: None,
71123
on_check_mic_clicked=lambda: None,
72124
actions=actions,

0 commit comments

Comments
 (0)