3033ca5b33
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].
173 lines
6.4 KiB
Swift
173 lines
6.4 KiB
Swift
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["Couldn’t 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["Couldn’t 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
|
||
}
|
||
}
|