Files
OSGKeyboard/OSGKeyboardUITests/PolishStylesGenerationUITests.swift
Rocky 3033ca5b33 fix(polish): gate test-build style generation at 1,250 chars and cap export at 5,000
Remove the test-mode unlimited personal-style corpus bypass and replace it with a real 1,250-character minimum gate. Production stays at 2,500. Training-corpus export and the diagnostics view now use a 5,000-character upper bound so the user's full private history never leaves the device in a single export.

- Add PolishStyleLearningCorpusBuilder.testBuildEffectiveCharacterCount (1,250) and trainingExtractionMaximumCharacterCount (5,000); trainingWindow(from:maximumCharacterCount:) accepts a custom cap.
- PolishStylesView: drop bypassesStyleLearningCharacterGate in favor of styleLearningMinimumCharacterCount, unify the ready/remaining text, remove the obsolete polishStyles.learn.testBuildReady key.
- PolishStyleCorpusExportStore: pass the 5,000 cap to trainingWindow and expose trainingExtractionMaximumCharacterCount in the export schema.
- FlowDiagnosticsSettingsView: show the export-sized (5,000-cap) corpus so the diagnostics match what the export ZIP actually contains.
- Tests: rewrite the export cap test for 5,000, add below-cap coverage, add trainingWindow(maximumCharacterCount:) unit coverage, and convert the old UI test into a regression test for the 1,250 test-build minimum.
- L10n: drop the testBuildReady string in en + zh-Hans, update diagnostics.corpus.ready to mention 5,000.
- CHANGELOG: bilingual entry under [Unreleased].
2026-08-30 12:09:06 +08:00

173 lines
6.4 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
final class PolishStylesGenerationUITests: XCTestCase {
func testTestBuildRequiresTwelveFiftyCharacterMinimum() {
// The old "unlimited" test-mode bypass has been removed. Test
// builds now enforce a 1,250-character minimum: an empty corpus
// must keep the generation button disabled, and the visible
// progress text should reflect the remaining distance to the
// active gate.
continueAfterFailure = false
let app = launchServiceHarness(
additionalArgument: "--polish-styles-service-ui-test-no-corpus"
)
let generate = app.buttons["polishStyles.learn.generate"]
XCTAssertTrue(generate.waitForExistence(timeout: 5))
XCTAssertFalse(
generate.isEnabled,
"Empty corpus in a test build must not unlock generation; the 1,250-character minimum still applies."
)
XCTAssertFalse(
app.staticTexts[
"Test build: 2,500-character limit disabled"
].exists,
"The old unlimited-bypass label must no longer be shown."
)
}
func testGeneratedStyleReviewAndSaveFlow() {
continueAfterFailure = false
let app = launchServiceHarness()
let generate = app.buttons["polishStyles.learn.generate"]
XCTAssertTrue(generate.waitForExistence(timeout: 5))
generate.tap()
let save = app.buttons["polishStyles.editor.save"]
XCTAssertTrue(save.waitForExistence(timeout: 8))
let promptEditor = app.textViews["polishStyles.editor.prompt"]
XCTAssertTrue(promptEditor.waitForExistence(timeout: 2))
XCTAssertTrue(
(promptEditor.value as? String)?.contains("natural, direct voice") == true,
"The real learning service must pass the scripted synthesis into review"
)
save.tap()
let learnedCard = app.descendants(matching: .any)[
"polishStyles.learnedStyle.card"
]
XCTAssertTrue(learnedCard.waitForExistence(timeout: 5))
XCTAssertTrue(app.staticTexts["Confidence: 86%"].exists)
}
func testInsufficientEvidenceIsDisclosedDuringReviewAndAfterSave() {
continueAfterFailure = false
let app = launchServiceHarness(usesInsufficientEvidence: true)
let generate = app.buttons["polishStyles.learn.generate"]
XCTAssertTrue(generate.waitForExistence(timeout: 5))
generate.tap()
let warning = app.staticTexts[
"Low confidence. The prompt was generated from limited evidence; please review it."
]
XCTAssertTrue(warning.waitForExistence(timeout: 8))
XCTAssertTrue(app.staticTexts["Confidence: 20%"].exists)
let save = app.buttons["polishStyles.editor.save"]
XCTAssertTrue(save.exists)
save.tap()
let learnedCard = app.descendants(matching: .any)[
"polishStyles.learnedStyle.card"
]
XCTAssertTrue(learnedCard.waitForExistence(timeout: 5))
XCTAssertTrue(warning.waitForExistence(timeout: 2))
}
func testRegenerationPreservesExistingStyleIdentityUntilSave() {
continueAfterFailure = false
let app = launchServiceHarness(
additionalArgument: "--polish-styles-service-ui-test-regenerate"
)
let learnedCard = app.descendants(matching: .any)[
"polishStyles.learnedStyle.card"
]
XCTAssertTrue(learnedCard.waitForExistence(timeout: 5))
let regenerate = app.buttons["Regenerate"]
XCTAssertTrue(regenerate.exists)
let originalID = regenerate.value as? String
XCTAssertFalse(originalID?.isEmpty ?? true)
regenerate.tap()
let save = app.buttons["polishStyles.editor.save"]
XCTAssertTrue(save.waitForExistence(timeout: 8))
XCTAssertEqual(
regenerate.value as? String,
originalID,
"The persisted style must remain unchanged while review is open"
)
save.tap()
XCTAssertTrue(learnedCard.waitForExistence(timeout: 5))
XCTAssertEqual(
app.buttons["Regenerate"].value as? String,
originalID
)
}
func testSynthesisFailureKeepsExistingStyleUnchanged() {
continueAfterFailure = false
let app = launchServiceHarness(
additionalArgument: "--polish-styles-service-ui-test-failure"
)
let learnedCard = app.descendants(matching: .any)[
"polishStyles.learnedStyle.card"
]
XCTAssertTrue(learnedCard.waitForExistence(timeout: 5))
let regenerate = app.buttons["Regenerate"]
let originalID = regenerate.value as? String
regenerate.tap()
XCTAssertTrue(
app.staticTexts["Couldnt Generate Style"].waitForExistence(timeout: 8)
)
XCTAssertEqual(regenerate.value as? String, originalID)
XCTAssertFalse(app.buttons["polishStyles.editor.save"].exists)
}
func testLeavingStyleScreenCancelsGenerationWithoutTimeoutAlert() {
continueAfterFailure = false
let app = launchServiceHarness(
additionalArgument: "--polish-styles-service-ui-test-cancel"
)
let generate = app.buttons["polishStyles.learn.generate"]
XCTAssertTrue(generate.waitForExistence(timeout: 5))
generate.tap()
let leave = app.buttons["polishStyles.test.leave"]
XCTAssertTrue(leave.exists)
leave.tap()
XCTAssertTrue(
app.staticTexts["polishStyles.test.closed"].waitForExistence(timeout: 3)
)
XCTAssertTrue(
app.staticTexts["polishStyles.test.cancelled"].waitForExistence(timeout: 3)
)
XCTAssertFalse(app.staticTexts["Couldnt Generate Style"].exists)
}
private func launchServiceHarness(
usesInsufficientEvidence: Bool = false,
additionalArgument: String? = nil
) -> XCUIApplication {
let app = XCUIApplication()
app.launchArguments = [
"--polish-styles-screenshot",
"--polish-styles-service-ui-test",
"--screenshot-lang=en"
]
if usesInsufficientEvidence {
app.launchArguments.append(
"--polish-styles-service-ui-test-insufficient"
)
}
if let additionalArgument {
app.launchArguments.append(additionalArgument)
}
app.launch()
return app
}
}