fix(print): enumerate all EPIJ_Qual quality options (#180) #194
@@ -24,6 +24,18 @@ final class CupsOptionsFilterTests: XCTestCase {
|
|||||||
XCTAssertEqual(CupsOptionsFilter.filter(raw), raw)
|
XCTAssertEqual(CupsOptionsFilter.filter(raw), raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #180 — a captured `EPIJ_Qual` (and the other canonical quality
|
||||||
|
/// keys) survives the filter so it wins over the Stage 2 explicit
|
||||||
|
/// quality in `LpArgs`.
|
||||||
|
func testKeepsQualityKeys() {
|
||||||
|
let raw = "EPIJ_Qual=304 CNIJPrintQuality=3 PrintQuality=2 "
|
||||||
|
+ "cupsPrintQuality=High Quality=Best "
|
||||||
|
+ "com.apple.print.JobTicket.PMTotalSidesImaged=0"
|
||||||
|
XCTAssertEqual(CupsOptionsFilter.filter(raw),
|
||||||
|
"EPIJ_Qual=304 CNIJPrintQuality=3 PrintQuality=2 "
|
||||||
|
+ "cupsPrintQuality=High Quality=Best")
|
||||||
|
}
|
||||||
|
|
||||||
func testKeepsUnknown() {
|
func testKeepsUnknown() {
|
||||||
let raw = "VendorFooBar=baz MediaType=Plain"
|
let raw = "VendorFooBar=baz MediaType=Plain"
|
||||||
XCTAssertEqual(CupsOptionsFilter.filter(raw), raw)
|
XCTAssertEqual(CupsOptionsFilter.filter(raw), raw)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ final class CupsParserTests: XCTestCase {
|
|||||||
MediaType/Media Type: *Stationery PhotographicHighGloss Photographic PhotographicMatte Envelope
|
MediaType/Media Type: *Stationery PhotographicHighGloss Photographic PhotographicMatte Envelope
|
||||||
ColorModel/Output Mode: *RGB Gray
|
ColorModel/Output Mode: *RGB Gray
|
||||||
Duplex/Duplex: *None DuplexNoTumble DuplexTumble
|
Duplex/Duplex: *None DuplexNoTumble DuplexTumble
|
||||||
cupsPrintQuality/cupsPrintQuality: Draft *Normal High
|
EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307
|
||||||
"""
|
"""
|
||||||
|
|
||||||
func testDestinations() {
|
func testDestinations() {
|
||||||
@@ -124,6 +124,12 @@ final class CupsParserTests: XCTestCase {
|
|||||||
// last (they are colour-ish keys on some drivers — #183/#180).
|
// last (they are colour-ish keys on some drivers — #183/#180).
|
||||||
XCTAssertEqual(CupsParsers.detectQualityKey(
|
XCTAssertEqual(CupsParsers.detectQualityKey(
|
||||||
optionKeys: ["EPIJ_Qual", "Quality", "OutputMode"]), "EPIJ_Qual")
|
optionKeys: ["EPIJ_Qual", "Quality", "OutputMode"]), "EPIJ_Qual")
|
||||||
|
// A full Epson key set — EPIJ_Qual wins over the colour-mode
|
||||||
|
// key, the generic keys, and Resolution (#180, R11).
|
||||||
|
XCTAssertEqual(CupsParsers.detectQualityKey(
|
||||||
|
optionKeys: ["EPIJ_Qual", "OutputMode", "Resolution",
|
||||||
|
"cupsPrintQuality", "PrintQuality",
|
||||||
|
"ColorModel"]), "EPIJ_Qual")
|
||||||
XCTAssertEqual(CupsParsers.detectQualityKey(
|
XCTAssertEqual(CupsParsers.detectQualityKey(
|
||||||
optionKeys: ["Quality", "OutputMode", "Resolution"]), "Quality")
|
optionKeys: ["Quality", "OutputMode", "Resolution"]), "Quality")
|
||||||
XCTAssertEqual(CupsParsers.detectQualityKey(
|
XCTAssertEqual(CupsParsers.detectQualityKey(
|
||||||
@@ -141,32 +147,65 @@ final class CupsParserTests: XCTestCase {
|
|||||||
let listings = CupsParsers.lpoptionsList(lpoptionsL)
|
let listings = CupsParsers.lpoptionsList(lpoptionsL)
|
||||||
let caps = service.capabilities(from: listings, ppd: nil)
|
let caps = service.capabilities(from: listings, ppd: nil)
|
||||||
|
|
||||||
// The fixture's only roster member is cupsPrintQuality.
|
// EPIJ_Qual is the roster member — all seven Epson codes
|
||||||
XCTAssertEqual(caps.qualityKey, "cupsPrintQuality")
|
// enumerate in the driver's own (non-sorted) order (#180).
|
||||||
XCTAssertEqual(caps.qualities.map(\.id), ["Draft", "Normal", "High"])
|
XCTAssertEqual(caps.qualityKey, "EPIJ_Qual")
|
||||||
XCTAssertEqual(caps.qualityDefault, "Normal")
|
XCTAssertEqual(caps.qualities.map(\.id),
|
||||||
|
["301", "302", "303", "308", "304", "305", "307"])
|
||||||
|
XCTAssertEqual(caps.qualityDefault, "303")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCapabilitiesQualityPpdLabels() {
|
func testCapabilitiesQualityPpdLabels() {
|
||||||
|
// Epson XP-55 PPD fragment — the seven `*EPIJ_Qual id/Label`
|
||||||
|
// lines in the driver's own order (#180).
|
||||||
let ppd = """
|
let ppd = """
|
||||||
*EPIJ_Qual 301/Draft: ""
|
*OpenUI *EPIJ_Qual/Print Quality: PickOne
|
||||||
|
*DefaultEPIJ_Qual: 303
|
||||||
|
*EPIJ_Qual 301/Fast Economy: ""
|
||||||
|
*EPIJ_Qual 302/Economy: ""
|
||||||
*EPIJ_Qual 303/Normal: ""
|
*EPIJ_Qual 303/Normal: ""
|
||||||
*EPIJ_Qual 308/High Speed: ""
|
*EPIJ_Qual 308/Draft: ""
|
||||||
|
*EPIJ_Qual 304/Fine: ""
|
||||||
|
*EPIJ_Qual 305/Quality: ""
|
||||||
|
*EPIJ_Qual 307/Best Quality: ""
|
||||||
|
*CloseUI: *EPIJ_Qual
|
||||||
"""
|
"""
|
||||||
let service = CupsService()
|
let service = CupsService()
|
||||||
let listings = CupsParsers.lpoptionsList(
|
let listings = CupsParsers.lpoptionsList(
|
||||||
"EPIJ_Qual/Print Quality: 301 *303 308\n")
|
"EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307\n")
|
||||||
let caps = service.capabilities(from: listings, ppd: ppd)
|
let caps = service.capabilities(from: listings, ppd: ppd)
|
||||||
|
|
||||||
XCTAssertEqual(caps.qualityKey, "EPIJ_Qual")
|
XCTAssertEqual(caps.qualityKey, "EPIJ_Qual")
|
||||||
XCTAssertEqual(caps.qualities, [
|
XCTAssertEqual(caps.qualities, [
|
||||||
PrinterQuality(id: "301", name: "Draft"),
|
PrinterQuality(id: "301", name: "Fast Economy"),
|
||||||
|
PrinterQuality(id: "302", name: "Economy"),
|
||||||
PrinterQuality(id: "303", name: "Normal"),
|
PrinterQuality(id: "303", name: "Normal"),
|
||||||
PrinterQuality(id: "308", name: "High Speed"),
|
PrinterQuality(id: "308", name: "Draft"),
|
||||||
|
PrinterQuality(id: "304", name: "Fine"),
|
||||||
|
PrinterQuality(id: "305", name: "Quality"),
|
||||||
|
PrinterQuality(id: "307", name: "Best Quality"),
|
||||||
])
|
])
|
||||||
XCTAssertEqual(caps.qualityDefault, "303")
|
XCTAssertEqual(caps.qualityDefault, "303")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #180 — the Epson listing also carries `OutputMode` (a colour
|
||||||
|
/// mode) and `Resolution`; detection must still pick `EPIJ_Qual`.
|
||||||
|
func testCapabilitiesQualityEpsonDetection() {
|
||||||
|
let service = CupsService()
|
||||||
|
let listings = CupsParsers.lpoptionsList("""
|
||||||
|
PageSize/Media Size: *A4 Letter
|
||||||
|
EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307
|
||||||
|
OutputMode/Color Mode: *Color Mono
|
||||||
|
Resolution/Resolution: *360dpi 720dpi
|
||||||
|
""")
|
||||||
|
let caps = service.capabilities(from: listings, ppd: nil)
|
||||||
|
|
||||||
|
XCTAssertEqual(caps.qualityKey, "EPIJ_Qual")
|
||||||
|
XCTAssertEqual(caps.qualities.count, 7)
|
||||||
|
XCTAssertEqual(caps.qualityDefault, "303")
|
||||||
|
XCTAssertFalse(caps.qualities.contains { $0.id == "Color" })
|
||||||
|
}
|
||||||
|
|
||||||
func testExtractOption() {
|
func testExtractOption() {
|
||||||
let options = "PageSize=A4 EPIJ_Qual=303 printer-info='EPSON XP-55'"
|
let options = "PageSize=A4 EPIJ_Qual=303 printer-info='EPSON XP-55'"
|
||||||
XCTAssertEqual(CupsParsers.extractOption(
|
XCTAssertEqual(CupsParsers.extractOption(
|
||||||
|
|||||||
@@ -106,12 +106,12 @@ final class LpArgsTests: XCTestCase {
|
|||||||
func testQualityDerived() throws {
|
func testQualityDerived() throws {
|
||||||
let argv = try build(
|
let argv = try build(
|
||||||
options: PrintOptions(
|
options: PrintOptions(
|
||||||
orientation: "portrait", mediaType: "Photo", quality: "303"),
|
orientation: "portrait", mediaType: "Photo", quality: "305"),
|
||||||
optionKeys: ["EPIJ_Qual", "MediaType"])
|
optionKeys: ["EPIJ_Qual", "MediaType"])
|
||||||
XCTAssertTrue(argv.contains("EPIJ_Qual=303"))
|
XCTAssertTrue(argv.contains("EPIJ_Qual=305"))
|
||||||
// Emit order: after the media option, before orientation.
|
// Emit order: after the media option, before orientation.
|
||||||
let media = argv.firstIndex(of: "MediaType=Photo")!
|
let media = argv.firstIndex(of: "MediaType=Photo")!
|
||||||
let quality = argv.firstIndex(of: "EPIJ_Qual=303")!
|
let quality = argv.firstIndex(of: "EPIJ_Qual=305")!
|
||||||
let orient = argv.firstIndex(of: "orientation-requested=3")!
|
let orient = argv.firstIndex(of: "orientation-requested=3")!
|
||||||
XCTAssertTrue(media < quality && quality < orient)
|
XCTAssertTrue(media < quality && quality < orient)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ final class PrintSessionViewModelTests: XCTestCase {
|
|||||||
printf 'PageSize/Media Size: 4x6 5x7 *A4 Letter Legal Custom.WIDTHxHEIGHT\\n'
|
printf 'PageSize/Media Size: 4x6 5x7 *A4 Letter Legal Custom.WIDTHxHEIGHT\\n'
|
||||||
printf 'InputSlot/Media Source: Auto *Main Rear\\n'
|
printf 'InputSlot/Media Source: Auto *Main Rear\\n'
|
||||||
printf 'MediaType/Media Type: *Stationery Glossy Matte\\n'
|
printf 'MediaType/Media Type: *Stationery Glossy Matte\\n'
|
||||||
printf 'EPIJ_Qual/Print Quality: 301 302 *303 304\\n'
|
printf 'EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307\\n'
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
printf "printer-info='Mock %s' printer-type=42\\n" "$queue"
|
printf "printer-info='Mock %s' printer-type=42\\n" "$queue"
|
||||||
@@ -106,6 +106,9 @@ final class PrintSessionViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(workflow.print.selectedPaperSize, 3)
|
XCTAssertEqual(workflow.print.selectedPaperSize, 3)
|
||||||
// Quality seeds from the `*` default on caps load.
|
// Quality seeds from the `*` default on caps load.
|
||||||
XCTAssertEqual(workflow.print.selectedQuality, "303")
|
XCTAssertEqual(workflow.print.selectedQuality, "303")
|
||||||
|
// All seven Epson codes enumerate in driver order (#180).
|
||||||
|
XCTAssertEqual(workflow.print.printerCaps.qualities.map(\.id),
|
||||||
|
["301", "302", "303", "308", "304", "305", "307"])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `.custom` → synthetic `id: 0` entry whose token is the
|
/// `.custom` → synthetic `id: 0` entry whose token is the
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Mock lpoptions for Milestone3UITests. `-p <q>` prints printer-info;
|
# Mock lpoptions for Milestone3UITests. `-p <q>` prints printer-info;
|
||||||
# `-p <q> -l` prints Key/Label listings incl. Epson bypass keys.
|
# `-p <q> -l` prints Key/Label listings incl. Epson bypass keys and the
|
||||||
|
# recorded XP-55 EPIJ_Qual line — the driver's own non-sorted order
|
||||||
|
# (308 sits between 303 and 304, #180).
|
||||||
queue=""
|
queue=""
|
||||||
list=0
|
list=0
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
@@ -15,7 +17,7 @@ if [ "$list" = "1" ]; then
|
|||||||
printf 'PageSize/Media Size: 4x6 5x7 *A4 Letter Legal\n'
|
printf 'PageSize/Media Size: 4x6 5x7 *A4 Letter Legal\n'
|
||||||
printf 'InputSlot/Media Source: Auto *Main Rear\n'
|
printf 'InputSlot/Media Source: Auto *Main Rear\n'
|
||||||
printf 'MediaType/Media Type: *Stationery PhotographicGlossy PhotographicMatte\n'
|
printf 'MediaType/Media Type: *Stationery PhotographicGlossy PhotographicMatte\n'
|
||||||
printf 'EPIJ_Qual/Print Quality: 301 302 *303 304 305 307 308\n'
|
printf 'EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307\n'
|
||||||
printf 'EPIJ_CMat/Color Adjust: *0 1 2 3\n'
|
printf 'EPIJ_CMat/Color Adjust: *0 1 2 3\n'
|
||||||
printf 'ColorModel/Output Mode: *RGB Gray\n'
|
printf 'ColorModel/Output Mode: *RGB Gray\n'
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -136,6 +136,32 @@ final class Milestone11PrintSettingsUITests: XCTestCase {
|
|||||||
XCTAssertEqual(selection(of: "printerQualitySelect"), "303")
|
XCTAssertEqual(selection(of: "printerQualitySelect"), "303")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #180 — the quality picker lists all seven Epson `EPIJ_Qual`
|
||||||
|
/// codes in the driver's own order (308 between 303 and 304); no
|
||||||
|
/// PPD is injected under UI testing so items show raw tokens.
|
||||||
|
func testQualityPickerListsAllSevenDriverOptions() throws {
|
||||||
|
launchAppWithDefaults()
|
||||||
|
reachPrintPanel()
|
||||||
|
_ = waitFor("printerStatusBadge")
|
||||||
|
|
||||||
|
let picker = app.popUpButtons["printerQualitySelect"]
|
||||||
|
XCTAssertTrue(picker.waitForExistence(timeout: 10))
|
||||||
|
picker.click()
|
||||||
|
|
||||||
|
let expected = ["301", "302", "303", "308", "304", "305", "307"]
|
||||||
|
for token in expected {
|
||||||
|
XCTAssertTrue(
|
||||||
|
app.menuItems[token].waitForExistence(timeout: 5),
|
||||||
|
"Missing quality menu item \(token)")
|
||||||
|
}
|
||||||
|
let titles = app.menuItems.allElementsBoundByIndex
|
||||||
|
.map(\.title)
|
||||||
|
.filter { expected.contains($0) }
|
||||||
|
XCTAssertEqual(titles, expected)
|
||||||
|
|
||||||
|
app.typeKey(XCUIKeyboardKey.escape, modifierFlags: [])
|
||||||
|
}
|
||||||
|
|
||||||
/// The stubbed panel result's captured `PageSize=`/`EPIJ_Qual=`
|
/// The stubbed panel result's captured `PageSize=`/`EPIJ_Qual=`
|
||||||
/// apply back into the Stage 2 pickers and reach the `lp` argv
|
/// apply back into the Stage 2 pickers and reach the `lp` argv
|
||||||
/// (R15 — the real modal is never driven).
|
/// (R15 — the real modal is never driven).
|
||||||
|
|||||||
Reference in New Issue
Block a user