Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:

- name: Install Wails v3 CLI
shell: bash
run: go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha2.103
run: go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha2.117

- name: Install golangci-lint v2
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:

- name: Install Wails v3 CLI
shell: bash
run: go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha2.103
run: go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha2.117

- name: Install frontend deps
shell: bash
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,29 @@ go 1.26.0
require (
github.com/google/uuid v1.6.0
github.com/pdfcpu/pdfcpu v0.12.1
github.com/wailsapp/wails/v3 v3.0.0-alpha2.103
github.com/wailsapp/wails/v3 v3.0.0-alpha2.117
)

require (
github.com/adrg/xdg v0.5.3 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.2 // indirect
github.com/hhrutter/tiff v1.0.3 // indirect
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/wailsapp/wails/webview2 v1.0.24 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/image v0.40.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.37.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
13 changes: 7 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6p
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e h1:Lf/gRkoycfOBPa42vU2bbgPurFong6zXeFtPoxholzU=
github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
Expand All @@ -27,8 +25,11 @@ github.com/hhrutter/tiff v1.0.3 h1:POV5xITOE1Lt5FvP24ylft0LyCmHmc8GkJ1SVlvUyk0=
github.com/hhrutter/tiff v1.0.3/go.mod h1:zZDLVY4cp9za2FLrryAaGszwWYAUM6DrRiBR0l//mxA=
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
Expand All @@ -41,18 +42,18 @@ github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3Ry
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/pdfcpu/pdfcpu v0.12.1 h1:HwoN72zJCj+pPbfMDChYBTZrT7SY0VwgUzqeaId3I20=
github.com/pdfcpu/pdfcpu v0.12.1/go.mod h1:7KPpVLMavcpliPrtN6o7Kuk3cFtYq8nii3SJnnsK7ps=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/wailsapp/wails/v3 v3.0.0-alpha2.103 h1:gL7MzpN3SFw3rVjusmyNXds9PutZBk/M/AsSyppfN4c=
github.com/wailsapp/wails/v3 v3.0.0-alpha2.103/go.mod h1:GRW1qYl54Zi/w1mjCzDrMiy76g2BLfNlpqF640hgMf0=
github.com/wailsapp/wails/webview2 v1.0.24 h1:uULnjCSaRfMlU84mS3kjLgPsRosEOIusVK1nFOHZHzs=
github.com/wailsapp/wails/webview2 v1.0.24/go.mod h1:sdf+s0nAdxlzVWf9SCxC15XaxnQPJeY+uU1Ucn3jHQM=
github.com/wailsapp/wails/v3 v3.0.0-alpha2.117 h1:udyjqPG3AIgkod5QDR/WblCkpV8R86BFPSrsWxSyt5Y=
github.com/wailsapp/wails/v3 v3.0.0-alpha2.117/go.mod h1:74WH2FScMsgucZvHHvv7eOefDXCm/CjuIxqhhZgPhKg=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8=
Expand Down
85 changes: 85 additions & 0 deletions tests/14-2-wails-alpha2-117-upgrade/bindings_contract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// AC3.2, AC3.3: consumer-driven binding-presence contract.
//
// scenario 14.2-INTG-002 [P0] (risk R-14-08): the REGENERATED
// frontend/bindings/ must still export every PDFService method the frontend
// actually imports. This gates on the CONTRACT, NOT on the informational
// 29-method / 38-model counts (a legitimate refactor can move those; the
// brittle-count pin was dropped at 12-3 per project_struct_grep_tests_brittle).
// A `wails3 generate bindings -clean=true` that drops or renames a consumer
// method under the alpha2.117 bump fails loud here; an unrelated surface change
// does not.
//
// This is a STANDING regression net: it passes on the current tree (the binding
// already exports these) and re-passes after the bump + regen (AC3.2's
// zero-diff / reconciled case), or fails loud if the regen moved the wire.
package story_14_2_wails_alpha2_117_upgrade_test

import (
"strings"
"testing"
)

// consumerBoundMethods is the explicit set of PDFService methods the frontend
// PRODUCTION code imports from the generated binding
// (frontend/bindings/.../pdfservice/pdfservice.js). A deliberate subset of the
// full receiver surface -- the consumer contract, not the whole API and not a
// count. Sourced from the non-test `import { ... } from
// '.../pdfservice/pdfservice.js'` sites (App.jsx, ObjectInfoPanel.tsx,
// DetailPanel.tsx, XRefTableView.tsx, CommandPalette.tsx, TreePanel.tsx,
// TabBar.tsx, useObjectIndex.ts, usePDFService.ts). If the dev adds/removes a
// frontend import in the same change, this list moves WITH the real consumer
// dependency -- which is exactly the contract we want, unlike a magic count that
// churns on every unrelated surface change.
var consumerBoundMethods = []string{
"CloseDocument",
"ConsumePendingOpenFiles",
"GetObjectSource",
"GetObjectDetail",
"GetContentStream",
"GetImageData",
"GetReverseRefs",
"GetFontView",
"GetXRefTable",
"GetAncestorPath",
"GetChildren",
"GetObjectIndex",
"OpenFile",
"GetTreeRoot",
"OpenFileDialog",
"GoToPage",
}

// bindingRelPath is the regenerated Wails JS binding for PDFService.
const bindingRelPath = "frontend/bindings/unidoc-pdf-debugger/internal/pdfservice/pdfservice.js"

// Test_14_2_INTG_002_BindingsExportConsumerMethods [P0] AC3.2/AC3.3: the
// (re)generated binding exports every method the frontend actually invokes.
// The presence contract that replaces the count==N pin. A `-clean=true` regen
// under the alpha2.117 bump that drops or renames a consumer method fails loud
// here; an unrelated change to the full method surface does NOT.
func Test_14_2_INTG_002_BindingsExportConsumerMethods(t *testing.T) {
if !fileExists(t, bindingRelPath) {
t.Fatalf("[P0] 14.2-INTG-002: regenerated binding %s must exist (run `wails3 generate bindings -clean=true`, AC3.2 / Task 2.2)", bindingRelPath)
}
src := readSource(t, bindingRelPath)
for _, m := range consumerBoundMethods {
needle := "export function " + m + "("
if !strings.Contains(src, needle) {
t.Errorf("[P0] 14.2-INTG-002: pdfservice.js must export consumer-bound method %q after regen -- the frontend imports it directly; a dropped/renamed binding silently breaks the UI (AC3.3 presence contract, NOT a count)", m)
}
}
}

// Test_14_2_INTG_002_BindingsDoNotResurrectGetPlainTextFull [P1] AC3.2: a
// `-clean=true` regen must not re-introduce the GetPlainTextFull symbol that
// Story 10-1 removed. Guards against a stale / non-clean regen re-baselining a
// dead binding under the bump.
func Test_14_2_INTG_002_BindingsDoNotResurrectGetPlainTextFull(t *testing.T) {
if !fileExists(t, bindingRelPath) {
t.Skipf("[P1] 14.2-INTG-002: %s missing -- see Test_14_2_INTG_002_BindingsExportConsumerMethods", bindingRelPath)
}
src := readSource(t, bindingRelPath)
if strings.Contains(src, "GetPlainTextFull") {
t.Errorf("[P1] 14.2-INTG-002: pdfservice.js must NOT export GetPlainTextFull -- 10-1 removed the Go method; a stale (`-clean=false`) regen re-introduces a dead binding (AC3.2)")
}
}
56 changes: 56 additions & 0 deletions tests/14-2-wails-alpha2-117-upgrade/deferred_gates_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Deferred / non-unit-automatable gate scenarios, encoded as documented
// skipped-with-reason placeholders rather than fabricated automation. These
// exist so the story's full gate is TRACEABLE in the suite (every 14.2 scenario
// has a home) without pretending a headless Go test can close a check that needs
// the wails3 toolchain, a git working tree, or real desktop hardware.
//
// Per the ATDD directive: follow the test pyramid, do NOT create E2E/browser
// tests for criteria that need full desktop/WebView interaction, and encode the
// bindings zero-diff check as a documented integration check (not a grep).
package story_14_2_wails_alpha2_117_upgrade_test

import "testing"

// Test_14_2_GATE_001_ZeroDiffBindings [P0] AC3.2 (risk R-14-08), THE acceptance
// criterion. DEFERRED: this is a process/git assertion, not a pure unit test.
// The check is: after `wails3 generate bindings -clean=true`, `git diff
// --exit-code frontend/bindings/` is EMPTY (or a non-empty diff has been fully
// reconciled and re-passed against 14.2-INTG-002/003). It requires the wails3
// CLI installed at the target pin and a clean git working tree -- neither
// available to a headless red-phase Go test. The Dev step runs it in CI/locally
// as a blocking gate and records the result in the Dev Agent Record; the
// presence contract (14.2-INTG-002) and wire-shape guard (14.2-INTG-003) are the
// automated nets that catch the specific drift a non-empty diff would carry.
func Test_14_2_GATE_001_ZeroDiffBindings(t *testing.T) {
t.Skip("[P0] 14.2-GATE-001: DEFERRED to the Dev/CI gate -- `wails3 generate bindings -clean=true` must yield an EMPTY `git diff frontend/bindings/` (or a reconciled diff re-passing 14.2-INTG-002/003). Needs the wails3 CLI at the target pin + a clean git tree; not automatable in a headless red-phase Go test. See 14.2-INTG-002 / 14.2-INTG-003 for the automated drift nets.")
}

// Test_14_2_GATE_002_ThreeRunnerBuildSmoke [P1] AC3.4 (risk R-14-07). DEFERRED:
// the 3-runner (ubuntu/macos/windows) `wails3 build` smoke proves compile+link,
// NOT runtime. It runs in the CI matrix with the wails3 CLI installed at the
// target pin; it is not a Go unit test and is not reproducible here.
func Test_14_2_GATE_002_ThreeRunnerBuildSmoke(t *testing.T) {
t.Skip("[P1] 14.2-GATE-002: DEFERRED to the CI 3-runner matrix -- host `wails3 build` on ubuntu/macos/windows (compile+link only, not runtime). Needs the wails3 CLI at the target pin; not automatable in this Go module.")
}

// Test_14_2_MANUAL_001_DesktopWebViewSmoke [P0] AC4 (risk R-14-07). DEFERRED
// HUMAN/HARDWARE GATE: the 5-step desktop smoke (launch/no-white-screen, load a
// real PDF round trip, exercise one method per binding category, one error path
// surfaces in the UI, clean quit) runs OUT-OF-BAND on real macOS + Windows
// hardware with a human observer. Headless CI cannot see the WebView. NO
// E2E/browser test is authored (Playwright cannot drive the native WebView).
// Evidence is captured as dated screenshots/recordings in the Dev Agent Record;
// the story does not reach `done` until that evidence is attached.
func Test_14_2_MANUAL_001_DesktopWebViewSmoke(t *testing.T) {
t.Skip("[P0] 14.2-MANUAL-001: DEFERRED human/hardware gate -- 5-step WebView smoke on real macOS + Windows, run out-of-band by a human, evidence in the Dev Agent Record (pending-human). Not automatable; no E2E authored per the test-pyramid directive.")
}

// Test_14_2_MANUAL_002_GuiRegressionSweep [P1] AC9/AC10 (risk R-14-10). DEFERRED
// HUMAN/HARDWARE GATE folded into the same manual pass as 14.2-MANUAL-001:
// window placement per OS (Windows first-launch centering), splash init, About
// dialog populates, WebView renders without artifacts; plus the AC10 record-only
// yes/no on the two Epic-15 framework-gated GUI items. Human observation on real
// hardware; not automatable, nothing implemented against either GUI primitive.
func Test_14_2_MANUAL_002_GuiRegressionSweep(t *testing.T) {
t.Skip("[P1] 14.2-MANUAL-002: DEFERRED human/hardware gate -- GUI regression sweep + AC10 record-only unblock check, folded into the manual pass on real macOS + Windows. Not automatable; evidence/record in the Dev Agent Record.")
}
3 changes: 3 additions & 0 deletions tests/14-2-wails-alpha2-117-upgrade/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module story-14-2-wails-alpha2-117-upgrade-tests

go 1.26
Loading