mirror of
https://github.com/logos-co/logos-basecamp.git
synced 2026-08-27 06:41:14 +00:00
Measured on the built artefact: main_ui.dylib DEFINES zero and IMPORTS zero of
TokenManager, StoreRegistry, LogosAPI, LogosAPIClient and logos_core_*, out of
3410 symbols read. It links Qt and nothing else from this workspace, which
nix/symbol-gate.nix enforces across the in-process image set.
Getting there needed the last non-Qt types off the boundary: the model
properties cross as QAbstractItemModel*, catalogInstallStageChanged carries an
int rather than InstallStage::Value, and the two prebuilt AppsFilterProxy
instances are declared in QML instead of owned by MainUIBackend. That last one
removes a real inversion -- PackageCoordinator called setRequiredPackages() on
a proxy the host held a pointer to; it now emits requiredPackagesResolved() and
QML binds to the republished property.
Window resolves the plugin, qobject_casts it to IShellView, checks
hostAbiVersion() against IShellHost_abi, and calls createShell(IShellHost*).
No error-label fallback widget: that degraded to something that looked like a
working app with an empty window.
Filter proxies read role constants off host-side models and InstallEnums is
used by nine host files, so app/interfaces/ gains the contract headers both
sides compile against -- the models inherit the role structs, leaving every
AppsModel::NameRole call site unchanged. The plugin's include path is
app/interfaces only, so including a host header does not compile.
Four things only running it finds:
* Logos::DesignSystem may be linked by exactly ONE image -- both linked it
and the app aborted with "Cannot add multiple registrations for
Logos.Icons"; QML module registration is process-global
* qmltyperegistrar emits no #include for a SOURCES header given as an
absolute path outside the project
* each qt_add_qml_module is its own target and inherits no include dirs
* AUTOMOC pairs header<->cpp by same-basename-same-DIRECTORY, which the
split breaks
tst_AppManagerView.qml grows five tests for the QML binding, checked with a
negative control: breaking one assertion fails qml-tests, so they run.
373 lines
20 KiB
YAML
373 lines
20 KiB
YAML
# ─────────────────────────────────────────────────────────────────────────────
|
|
# WIP — package-manager install flow not yet covered here.
|
|
#
|
|
# What we ultimately want: drive a real install through the bundle's package
|
|
# manager — open the Package Manager sidebar entry, browse the live catalog,
|
|
# select the accounts module + accounts UI, click Install, and assert the
|
|
# packages install. That sequence is currently REMOVED from this spec so the
|
|
# test stays green; this file only walks the sidebar and opens the package
|
|
# manager (screenshot).
|
|
#
|
|
# Why it's not here yet (see the session that trimmed it):
|
|
# - Headless, the catalog refresh chain runs (getReleases → getCategories →
|
|
# getPackages → getInstalledPackages → getValidVariants) but the final
|
|
# getValidVariants reply never gets back to the ui-host backend, so
|
|
# setIsLoading(false) never fires and the PM view is wedged on its loading
|
|
# state — the catalog rows never render, so there is nothing to click.
|
|
# - On top of that, finding/clicking catalog rows and the Install button by
|
|
# visible TEXT via findByProperty is fragile: the package manager view is
|
|
# IPC-driven and animating, and text search times out against it.
|
|
#
|
|
# Likely path to make it testable:
|
|
# - Have the package-manager-ui QML expose stable `objectName`s on the catalog
|
|
# rows, the per-row select control, and the Install / confirm buttons, then
|
|
# drive them via the `call_method` / objectName actions (as we already do for
|
|
# "pmui.BackendStore".selectRelease) instead of text-based clicks. objectName
|
|
# lookups are exact and don't depend on rendered text.
|
|
# - And fix the headless getValidVariants-reply-lost issue above so the catalog
|
|
# actually populates (an app/runtime fix, not a doc-test change).
|
|
#
|
|
# Until both are addressed, keep the install steps out of this spec. The earlier
|
|
# install sequence is recoverable from git history (the deleted
|
|
# basecamp-package-manager.test.yaml) if you want a starting point.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
name: "Exploring Installed Apps and Loaded Modules in the Basecamp Bundle"
|
|
output: basecamp-modules-bundle.md
|
|
release: ""
|
|
|
|
intro: |
|
|
> **⚠️ Work in progress.** This doc-test currently only walks the sidebar and
|
|
> opens the package manager. The goal is to also drive a **real install**
|
|
> through the package manager — open the Package Manager, browse the live
|
|
> catalog, select the **accounts module** and **accounts UI**, click
|
|
> **Install**, and verify they install — but that flow isn't covered yet.
|
|
> Headless, the catalog never finishes populating (the `getValidVariants`
|
|
> reply doesn't get back to the package-manager-ui backend, so the view stays
|
|
> stuck loading), and finding the catalog rows / Install button by visible
|
|
> text is unreliable. The likely fix is to expose stable **`objectName`s** on
|
|
> those UI elements so the test can target them directly (as it already does
|
|
> for `pmui.BackendStore`), plus an app-side fix for the stuck catalog load.
|
|
|
|
[`logos-basecamp`](https://github.com/logos-co/logos-basecamp) is the Logos
|
|
desktop application shell — a Qt/QML app with a sidebar of section buttons
|
|
(Workspace, Applications, Package Manager, Settings), an in-app **Applications**
|
|
catalog, and a **Settings** view whose **Apps Inspector** and **Module
|
|
Inspector** sub-sections list the UI plugins installed in the app and the core
|
|
modules loaded into its runtime. This doc-test builds **this** basecamp commit
|
|
as its real, shippable **portable bundle**, launches that bundle headless, and
|
|
drives its UI to inspect what is installed and loaded out of the box.
|
|
|
|
This is the bundle twin of the dev-app doc-test
|
|
([`basecamp-modules`](./basecamp-modules.md)): the exact same sidebar walk,
|
|
but driving the self-contained `#bin-bundle-dir` directory you ship instead of
|
|
the plain `nix build` dev binary. The bundle carries the app, its Qt runtime,
|
|
and a starter set of pre-installed modules/plugins (the main UI, the package
|
|
manager, package downloader, capability module, and the `package_manager_ui`
|
|
plugin) all inside it — no `/nix/store` needed at runtime.
|
|
|
|
The shipping bundle is `#bin-bundle-dir`, with the QML inspector compiled OFF.
|
|
To automate the UI here we build the inspector-enabled twin
|
|
(`#bin-bundle-dir-inspector` — `dirBundler appDistributedWithInspector` in
|
|
`flake.nix`): the *same* bundle, just compiled with the QML inspector switched
|
|
on so [`logos-qt-mcp`](https://github.com/logos-co/logos-qt-mcp) can drive it
|
|
headlessly. The inspector is a compile-time switch — off in the shipping
|
|
Release build, on here.
|
|
|
|
The flow this doc-test exercises is the everyday "what's in my app?" check:
|
|
|
|
1. Launch the bundle and land on the **Workspace** (the empty MDI tab area).
|
|
2. Open the **Applications** view from the sidebar — basecamp's catalog of
|
|
installable apps.
|
|
3. Open the **Package Manager** from the sidebar — clicking the sidebar entry
|
|
opens the bundled `package_manager_ui` plugin directly into the main
|
|
content area.
|
|
4. Open **Settings → Apps Inspector** — the apps (UI plugins) installed in
|
|
basecamp, e.g. the main UI and the package manager UI.
|
|
5. Switch to **Settings → Module Inspector** — the background modules loaded
|
|
into the runtime (package manager, package downloader, capability module)
|
|
shown as **Loaded** with live CPU/memory stats.
|
|
|
|
The bundle is launched headless (`QT_QPA_PLATFORM=offscreen`) and driven through
|
|
its QML inspector: we wait for elements, click sidebar items and sections,
|
|
assert on the rendered text, and capture a screenshot at each step. Because the
|
|
bundle is built from the commit under test, a green run is real evidence that
|
|
this change still produces a launchable, self-contained app whose Settings
|
|
inspectors report the installed plugins and loaded modules.
|
|
|
|
what_you_build: "This `logos-basecamp` commit, built as the real portable bundle (inspector-enabled `#bin-bundle-dir-inspector`) and driven through its sidebar (Applications, Package Manager, Settings → Apps Inspector / Module Inspector) to inspect the installed UI plugins and the loaded core modules."
|
|
|
|
what_you_learn:
|
|
- How basecamp's portable `#bin-bundle-dir` packages the app, its Qt runtime, and a starter set of modules into one self-contained directory
|
|
- How the inspector-enabled bundle variant lets logos-qt-mcp drive the real shippable app
|
|
- How the sidebar navigates between the **Workspace** (MDI), **Applications** catalog, **Package Manager**, and **Settings**
|
|
- How **Settings → Apps Inspector** lists the apps (UI plugins) installed in basecamp
|
|
- How **Settings → Module Inspector** shows which runtime modules are loaded, with live CPU/memory stats
|
|
- How the **Package Manager** sidebar entry opens the bundled `package_manager_ui` plugin directly into the main content area
|
|
- How to drive a headless Qt/QML app with logos-qt-mcp (wait, click, assert) and capture screenshots
|
|
|
|
prerequisites:
|
|
- |
|
|
**Nix** with flakes enabled. Install from [nixos.org](https://nixos.org/download.html), then enable flakes:
|
|
|
|
```bash
|
|
mkdir -p ~/.config/nix
|
|
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
|
|
```
|
|
|
|
Verify: `nix flake --help >/dev/null 2>&1 && echo "Flakes enabled"`
|
|
- "**A Linux or macOS machine.** The bundle runs headless via `QT_QPA_PLATFORM=offscreen`, so no display is required. No network access is needed — everything inspected here ships inside the bundle."
|
|
|
|
sections:
|
|
- title: "Dev app vs. portable bundle"
|
|
text: |
|
|
basecamp has two main build outputs:
|
|
|
|
```
|
|
#app -> result/bin/LogosBasecamp the development app
|
|
result/modules/ + plugins/ baked in next to the binary
|
|
QML inspector ON by default
|
|
|
|
#bin-bundle-dir -> the portable, self-contained bundle you ship (this doc-test)
|
|
Qt runtime + modules + plugins carried inside
|
|
QML inspector OFF in the shipping build
|
|
```
|
|
|
|
This doc-test uses the **bundle** — the self-contained directory basecamp
|
|
ships. Its starter modules and UI plugins are carried inside the bundle
|
|
under `modules/` (core modules) and `plugins/` (UI plugins), and the app
|
|
discovers them relative to its own binary on launch. The
|
|
**Settings → Apps Inspector** and **Settings → Module Inspector**
|
|
sub-views are the in-app windows into those two lists; the **Package
|
|
Manager** sidebar entry surfaces the bundled `package_manager_ui` plugin
|
|
directly. (The companion [`basecamp-modules`](./basecamp-modules.md)
|
|
doc-test runs the identical walk against the `#app` dev build.)
|
|
|
|
- title: "Build the qt-mcp test driver"
|
|
step: true
|
|
text: |
|
|
[`logos-qt-mcp`](https://github.com/logos-co/logos-qt-mcp) is the harness the
|
|
doc-test uses to connect to the bundle's QML inspector and drive it. Build it
|
|
once and link it as `./result-mcp`.
|
|
steps:
|
|
- title: "Build logos-qt-mcp"
|
|
# QT_MCP_FLAKE lets you point this at a local logos-qt-mcp checkout to
|
|
# validate an inspector change before it is published, e.g.
|
|
# export QT_MCP_FLAKE=path:/abs/path/to/logos-qt-mcp
|
|
# Unset (CI / normal runs) it builds the published flake shown below.
|
|
run: 'nix build "${QT_MCP_FLAKE:-github:logos-co/logos-qt-mcp}" -o result-mcp'
|
|
code_block: "nix build 'github:logos-co/logos-qt-mcp' -o result-mcp"
|
|
check_file: "result-mcp"
|
|
|
|
- title: "Build the basecamp bundle"
|
|
step: true
|
|
text: |
|
|
Build **this** basecamp commit's portable bundle — the self-contained
|
|
`#bin-bundle-dir` directory you ship — and link it as `./result-bundle`. The
|
|
build compiles the app and every bundled module/plugin through Nix, so the
|
|
first build is slow (subsequent runs hit the cache).
|
|
|
|
> To automate the UI, the doc-test actually builds the inspector-enabled
|
|
> twin of this bundle (`#bin-bundle-dir-inspector`) — identical except the
|
|
> QML inspector is compiled in. The inspector is never part of a shipping
|
|
> build; the command shown below is the real `#bin-bundle-dir` one you would
|
|
> run.
|
|
|
|
> The `{release}` in the URL pins the build to a specific commit: the
|
|
> doc-test runner expands it to a concrete ref. Locally that is this
|
|
> checkout's `HEAD` (see `run.sh`); in CI it is the commit being tested.
|
|
> With no pin it falls back to the latest `master`.
|
|
steps:
|
|
- title: "Build the bundle"
|
|
text: |
|
|
This is how you build the portable bundle you ship — the same command a
|
|
release build uses:
|
|
# NOTE: The command *shown* to the reader (code_block) is the normal
|
|
# shipping bundle, `#bin-bundle-dir`. The command actually *run* builds
|
|
# `#bin-bundle-dir-inspector` — the identical bundle compiled with the QML
|
|
# inspector switched on, so logos-qt-mcp can connect and drive the UI
|
|
# headlessly. The inspector is OFF in the shipping bundle; everything else
|
|
# about the two is the same.
|
|
run: "nix build 'github:logos-co/logos-basecamp{release}#bin-bundle-dir-inspector' -o result-bundle"
|
|
code_block: |
|
|
# From inside the clone this is simply: nix build '.#bin-bundle-dir'
|
|
nix build 'github:logos-co/logos-basecamp{release}#bin-bundle-dir' -o result-bundle
|
|
check_file: "result-bundle/bin/LogosBasecamp"
|
|
post_text: |
|
|
The bundle is self-contained: the launcher lives at
|
|
`result-bundle/bin/LogosBasecamp`, with `lib/` (Qt + app libraries),
|
|
`modules/` (bundled core modules) and `plugins/` (bundled UI plugins)
|
|
all alongside it.
|
|
|
|
> This doc-test builds the same bundle with the QML inspector enabled so
|
|
> it can automate the UI below. The inspector is **not** part of shipping
|
|
> builds — `#bin-bundle-dir` (shown above) is what you release.
|
|
|
|
- title: "Confirm the bundled UI plugins"
|
|
text: |
|
|
The UI plugins **Settings → Apps Inspector** will list are carried
|
|
under `plugins/` — confirm the package manager UI shipped in the
|
|
bundle. Basecamp's *own* shell is deliberately NOT here: it is
|
|
carried as a `main_ui` plugin again — one that links Qt and nothing
|
|
else, so the assertion below is that `main_ui` is PRESENT.
|
|
run: "ls result-bundle/plugins"
|
|
expect_contains:
|
|
- "package_manager_ui"
|
|
extra_run:
|
|
run: "ls result-bundle/plugins | grep -qx main_ui && echo 'main_ui present' || echo 'main_ui MISSING'"
|
|
expect_contains:
|
|
- "main_ui present"
|
|
|
|
- title: "Confirm the bundled core modules"
|
|
text: |
|
|
The core modules the runtime loads are carried under `modules/` —
|
|
confirm the package manager, package downloader, and capability module
|
|
are present:
|
|
run: "ls result-bundle/modules"
|
|
expect_contains:
|
|
- "package_manager"
|
|
- "package_downloader"
|
|
- "capability_module"
|
|
|
|
- title: "Launch the bundle and walk its sidebar"
|
|
step: true
|
|
text: |
|
|
Launch the bundle binary headless and drive its UI: confirm the sidebar
|
|
renders, open **Applications**, open the **Package Manager**, then go into
|
|
**Settings** and walk both **Apps Inspector** (installed UI plugins) and
|
|
**Module Inspector** (loaded core modules) — each interaction a real
|
|
round-trip through this basecamp bundle.
|
|
steps:
|
|
- title: "Launch and drive the bundle"
|
|
ui_test:
|
|
launch: "./result-bundle/bin/LogosBasecamp"
|
|
qt_mcp: "result-mcp"
|
|
# The bundle is already built (previous step), so the launch only has to
|
|
# boot the binary and open the inspector port — allow time for a cold
|
|
# first boot before the inspector is expected to answer.
|
|
launch_timeout: 300
|
|
tests:
|
|
# The app starts on the Workspace (MDI tab area). The sidebar is
|
|
# always visible — wait for one of its section labels to confirm
|
|
# the QML tree is up.
|
|
- name: "App window opens with the sidebar visible"
|
|
action: wait_for
|
|
texts: ["Applications", "Package Manager", "Settings"]
|
|
timeout: 60000
|
|
text: "The bundle launches headless. The sidebar renders with the **Applications**, **Package Manager**, and **Settings** entries."
|
|
screenshot: "bundle-launch.png"
|
|
|
|
# Click the Applications sidebar entry → AppManagerView renders.
|
|
- name: "Open the Applications view"
|
|
action: click
|
|
target: "Applications"
|
|
|
|
- name: "Applications view loads"
|
|
action: wait_for
|
|
texts: ["Install and manage applications."]
|
|
timeout: 30000
|
|
text: "The **Applications** view lists the apps you can install and the ones already on disk."
|
|
screenshot: "bundle-applications.png"
|
|
|
|
# Click the Package Manager sidebar entry → MainContainer routes
|
|
# the click to stack slot 2, which hosts the bundled
|
|
# package_manager_ui plugin's sandboxed QQuickWidget. We don't
|
|
# assert on PMUI's own QML text — its QQmlEngine is sandboxed and
|
|
# the catalog IPC can stall a deep inspector traversal — so click,
|
|
# let it render, and capture a screenshot.
|
|
- name: "Open the Package Manager"
|
|
action: click
|
|
target: "Package Manager"
|
|
|
|
- name: "Let the Package Manager view render, then capture it"
|
|
action: sleep
|
|
ms: 10000
|
|
text: "Clicking **Package Manager** opens the bundled `package_manager_ui` plugin in-place."
|
|
screenshot: "bundle-package-manager.png"
|
|
|
|
# Now into Settings to inspect plugins + core modules.
|
|
- name: "Open Settings"
|
|
action: click
|
|
target: "Settings"
|
|
|
|
- name: "Settings view loads"
|
|
action: wait_for
|
|
texts: ["Manage modules, apps and dashboards.", "Sections"]
|
|
timeout: 30000
|
|
text: "The **Settings** view loads with its left rail (Sections) and Dashboard selected by default."
|
|
screenshot: "bundle-settings.png"
|
|
|
|
# Click the Apps Inspector entry in the Settings left rail.
|
|
# find_and_click lands on it; the section title is unique.
|
|
- name: "Open Settings → Apps Inspector"
|
|
action: click
|
|
target: "Apps Inspector"
|
|
|
|
- name: "Apps Inspector loads"
|
|
action: wait_for
|
|
texts: ["Apps Inspector", "UI plugins available in this installation."]
|
|
timeout: 30000
|
|
text: "The **Settings → Apps Inspector** section lists the UI plugins installed in the app, with the same page-level search + Reload controls as the App Manager panel."
|
|
screenshot: "bundle-apps-inspector.png"
|
|
|
|
# Module rows render `displayName || name`; package_manager_ui's
|
|
# display_name is "Package Manager". Basecamp's own shell is no
|
|
# longer an installed app -- it is compiled into the executable --
|
|
# so there is no "Main UI" row to wait for.
|
|
- name: "Installed apps are listed"
|
|
action: wait_for
|
|
texts: ["Package Manager"]
|
|
timeout: 30000
|
|
text: "The installed apps appear as rows — here, the package manager UI."
|
|
screenshot: "bundle-apps-inspector-list.png"
|
|
|
|
# Switch to Module Inspector — the background modules loaded into
|
|
# the runtime. Selecting this section triggers backend.refreshCoreModules().
|
|
- name: "Open Settings → Module Inspector"
|
|
action: click
|
|
target: "Module Inspector"
|
|
|
|
- name: "Module Inspector loads"
|
|
action: wait_for
|
|
texts: ["Module Inspector", "Core modules known to the runtime, with live resource usage."]
|
|
timeout: 30000
|
|
text: "The **Settings → Module Inspector** section shows the background modules loaded into the runtime, with live resource usage."
|
|
screenshot: "bundle-module-inspector.png"
|
|
|
|
# Core modules render `displayName || name`; package_manager and
|
|
# capability_module both set display_name in their metadata.
|
|
- name: "Core modules are present"
|
|
action: wait_for
|
|
texts: ["Package Manager", "Capabilities"]
|
|
timeout: 30000
|
|
|
|
- name: "Core modules report as loaded"
|
|
action: wait_for
|
|
texts: ["Loaded", "Unload"]
|
|
timeout: 30000
|
|
text: "The bundled core modules report a **Loaded** status badge with an Unload action."
|
|
screenshot: "bundle-core-loaded.png"
|
|
|
|
# Give the stats timer (polls logos_core_get_module_stats every ~2s)
|
|
# a tick so the rendered rows carry live CPU/Mem values, then capture.
|
|
- name: "Let the module stats populate"
|
|
action: sleep
|
|
ms: 3000
|
|
text: "After a stats tick, each loaded module row carries live CPU / memory usage."
|
|
screenshot: "bundle-core-stats.png"
|
|
post_text: |
|
|
Every step here is a real action against this basecamp bundle: the
|
|
sidebar renders with **Applications**, **Package Manager**, and
|
|
**Settings** entries; opening **Applications** shows the install
|
|
catalog; clicking **Package Manager** opens the bundled
|
|
`package_manager_ui` plugin in-place (captured as a screenshot, since
|
|
its sandboxed QML can't be safely walked by the inspector);
|
|
**Settings → Apps Inspector** lists the installed apps
|
|
(`package_manager_ui`), and **Settings → Module Inspector** reports
|
|
the bundled runtime modules (`package_manager`, `package_downloader`,
|
|
`capability_module`) as **Loaded** with live CPU/memory stats. A
|
|
green run is end-to-end proof that this commit builds a launchable,
|
|
self-contained bundle whose Settings inspectors faithfully report what
|
|
is installed and loaded. The captured screenshots are embedded above
|
|
and in the CI report.
|