Share Extension の UI Test / UI Test for Share Extension

Af64bc38c0ffcfcabdf430759ee491ce?s=47 Elvis Shi
January 24, 2020

Share Extension の UI Test / UI Test for Share Extension

Share Extension を対応したとき、共有メニューの UI テストをどうすればいいのか

Af64bc38c0ffcfcabdf430759ee491ce?s=128

Elvis Shi

January 24, 2020
Tweet

Transcript

  1. 4IBSF&YUFOTJPOͷ6*5FTU for )","5"5FTU/JHIU

  2. } var employedBy = "YUMEMI Inc." var job = "iOS

    Tech Lead" var favoriteLanguage = "Swift" var twitter = "@lovee" var qiita = "lovee" var github = "el-hoshino" var additionalInfo = """ ཏখࠇઓهʢϩγϟΦϔΠηϯΩʣөը؍Α͏ʂ """ final class Me: Developable, Talkable {
  3. None
  4. None
  5. None
  6. None
  7. 4IBSF&YUFOTJPOΛରԠͨ͠ɻ 6*5FTUΛ͍ͨ͠ɻ Ͳ͏͢Ε͹͍͍ʁ

  8. func testQRCodeGenerationFromSafari() { let app = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari") app.launch() XCTContext.runActivity(named:

    "Go to about:blank page") { _ -> Void in let urlBar = app.otherElements["topBrowserBar"] urlBar.tap() urlBar.typeText("about:blank") app.buttons["Go"].tap() } XCTContext.runActivity(named: "Call Share menu") { _ -> Void in let shareButton = app.buttons["Share"] shareButton.tap() } XCTContext.runActivity(named: "Open QuickshaRe") { _ -> Void in let shareList = app.otherElements["ActivityListView"] XCTAssert(shareList.waitForExistence(timeout: 2)) let cell = shareList.cells.matching(identifier: "Activity").allElementsBoundByIndex[1] cell.tap() } XCTContext.runActivity(named: "Check label and image display") { _ -> Void in let view = app.otherElements["Share View"] let label = view.staticTexts["about:blank"] let image = view.images["QR Image"] XCTAssert(view.waitForExistence(timeout: 2)) XCTAssert(label.exists) XCTAssert(image.exists) let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil)! let qrCIImage = CIImage(image: image.screenshot().image)! let features = detector.features(in: qrCIImage) as! [CIQRCodeFeature] XCTAssertEqual(features[0].messageString, "about:blank") } }
  9. 6*5FTUͱ6OJU5FTUͷҧ͍ w 6OJU5FTUɿϝιου౳ͷॲཧʹର͢ΔϩδοΫͷςετ w ΞϓϦຊମΛ@testable import͠ɺϝιου΍ΦϒδΣΫτ ౳ͷςετର৅Λ࣮ࡍʹݺͼग़ͯ͠ಈ࡞Λ֬ೝ w ඞཁʹԠͯ͡ςετର৅ʹςετ޲͚ͷґଘΛ஫ೖՄೳ w

    ΞϓϦͷ࣮૷Λʢ͋Δఔ౓ʣ೺Ѳͨ͠ϗϫΠτϘοΫεςετ w 6*5FTUɿϏϧυࡁΈͷΞϓϦΛ௚઀ಈ͔͢ςετ w ΞϓϦຊମΛ@testable importͤͣʹ௚઀ΞϓϦΛૢ࡞ͯ͠ ಈ࡞Λ֬ೝ w ςετ޲͚ͷґଘ஫ೖ͸೉͍͠ w ΞϓϦͷ࣮૷Λશ͘೺Ѳ͠ͳ͍ϒϥοΫϘοΫεςετ
  10. ς ε τ ର ৅ ͷ  ຊ ମ Ξ

    ϓ Ϧ ࣮ ࡍ ͷ  ς ε τ λ ʔ ή ỽ τ ͔ Β  Ϗ ϧ υ ͠ ͨ Ξ ϓ Ϧ
  11. 2VJDLTIB3F ։͚ ͸͍

  12. 4BGBSJ։͚ ͸͍

  13. let app = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari") app.launch()

  14. None
  15. ΞϓϦ͸։͚ͨɻ Ͳ͏ૢ࡞͢Ε͹͍͍ͷʁ

  16. .staticTexts["Some Text"] .textFields["Placeholder Text"] .buttons["Button Title"] .images["Accessibility Identifier"] .otherElements["Identifier"] .exists

    .waitForExistence(timeout: 2) .tap() .typeText("Some Typing") // etc...
  17. .staticTexts["Some Text"] .textFields["Placeholder Text"] .buttons["Button Title"] .images["Accessibility Identifier"] .otherElements["Identifier"] .exists

    .waitForExistence(timeout: 2) .tap() .typeText("Some Typing") // etc... ͜ΕΒͷ"1*Λ૊Έ߹Θͤͯ ΞϓϦʹରͯ͠র߹΍ૢ࡞Λ༩͑Δ
  18. ڞ༗ϝχϡʔରԠͷ6*5FTU֬ೝखॱ 4BGBSJΛࢦఆͷ63-ʹભҠ ಡΈࠐΈ͕ऴΘͬͨΒڞ༗ϝχϡʔΛ։͘ ڞ༗ϝχϡʔ͔Β֘౰ΞϓϦΛ։͘ ։͍ͨը໘͕ਖ਼͘͠දࣔͰ͖͍ͯΔ͔ΛνΣοΫ

  19. εςοϓɿࢦఆͷ63-ʹભҠ ϒϥ΢βόʔΛԡ͢ ϒϥ΢βόʔʹࢦఆͷ63-Λೖྗ ΩʔϘʔυͷA(PAΩʔΛԡ͢

  20. // ϒϥ΢βը໘ͷ্ʹ͋Δϒϥ΢βόʔΛ୳͢ let urlBar = app.otherElements["topBrowserBar"] // ͦͷϒϥ΢βόʔΛԡ͢ urlBar.tap() //

    ϒϥ΢βόʔʹࢦఆͷจࣈΛೖΕΔ urlBar.typeText("about:blank") // ΩʔϘʔυͷ `Go` ΩʔΛԡ͢ app.buttons["Go"].tap()
  21. None
  22. εςοϓɿڞ༗ϝχϡʔΛ։͘ ڞ༗ϘλϯΛԡ͢

  23. // ΞϓϦͷڞ༗ϘλϯΛ୳͢ let shareButton = app.buttons["Share"] // ͦͷڞ༗ϘλϯΛԡ͢ shareButton.tap()

  24. None
  25. εςοϓɿ֘౰ΞϓϦΛ։͘ ڞ༗ϝχϡʔ͕දࣔ͞ΕΔ·Ͱ଴ͭ ڞ༗ϝχϡʔ಺ͷ֘౰ΞϓϦͷΞΠίϯΛԡ͢

  26. // ڞ༗ϝχϡʔΛ୳͢ let shareList = app.otherElements["ActivityListView"] // ڞ༗ϝχϡʔ͕දࣔ͞ΕΔ·Ͱ࠷େ 2 ඵ଴ͭ

    XCTAssert(shareList.waitForExistence(timeout: 2)) // ͦͷڞ༗ϝχϡʔ͔Β֘౰ΞϓϦͷΞΠίϯΛ୳͢ let cell = shareList.cells.matching(identifier: "Activity") .allElementsBoundByIndex[1] // ͦͷΞΠίϯΛԡ͢ cell.tap()
  27. None
  28. εςοϓɿਖ਼͘͠දࣔͰ͖͍ͯΔ͔ ࢦఆͷ63-ςΩετද͕ࣔ͋Δ 23ίʔυը૾ද͕ࣔ͋Δ 23ίʔυΛಡΈऔΕ͹ࢦఆͷ63-ςΩετͰ͋Δ

  29. // Share View Λ୳͢ let view = app.otherElements["Share View"] //

    ࢦఆͷςΩετදࣔΛ୳͢ let label = view.staticTexts["about:blank"] // QR ίʔυը૾දࣔΛ୳͢ let image = view.images["QR Image"] // Share View ͕දࣔ͞ΕΔ·Ͱ࠷େ 2 ඵ଴ͭ XCTAssert(view.waitForExistence(timeout: 2)) // ࢦఆͷ URL ςΩετද͕ࣔ͋Δ͜ͱΛ֬ೝ XCTAssert(label.exists) // QR ίʔυը૾ද͕ࣔ͋Δ͜ͱΛ֬ೝ XCTAssert(image.exists) // ͦͷը૾දࣔΛ QR ίʔυͱͯ͠ಡΈऔΕ͹ࢦఆͷ URL ςΩετͰ͋Δ͜ͱΛ֬ೝ let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil)! let qrCIImage = CIImage(image: image.screenshot().image)! let features = detector.features(in: qrCIImage) as! [CIQRCodeFeature] XCTAssertEqual(features[0].messageString, "about:blank") ౰વͳ͕Β JNQPSU$PSF*NBHF ͕ඞཁ
  30. None
  31. // ڞ༗ϝχϡʔΛ୳͢ let shareList = app.otherElements["ActivityListView"] // ڞ༗ϝχϡʔ͕දࣔ͞ΕΔ·Ͱ࠷େ 2 ඵ଴ͭ

    XCTAssert(shareList.waitForExistence(timeout: 2)) // ͦͷڞ༗ϝχϡʔ͔Β֘౰ΞϓϦͷΞΠίϯΛ୳͢ let cell = shareList.cells.matching(identifier: "Activity") .allElementsBoundByIndex[1] // ͦͷΞΠίϯΛԡ͢ cell.tap() ͳΜ͡Ό͜Εʂʁ NBUDIJOHʁ "DUJWJUZʁ <>ʁ
  32. 6*5FTUͰͲ͏΍ͬͯ ը໘ཁૉΛಛఆ͢Δͷ͔

  33. // ࢦఆͷςΩετදࣔΛ୳͢ let label = view.staticTexts["about:blank"] ςΩετͳΒ

  34. ςΩετͳΒ wͦͷ··["ݻఆςΩετ"]ͰׂΓग़͢ • staticTexts["Text"] • buttons["Button Title"] ˞෺ʹΑͬͯͳ͔ͥऔΕͳ͍ը໘ཁૉ΋͋Δ

  35. // QR ίʔυը૾දࣔΛ୳͢ let image = view.images["QR Image"] ςΩετҎ֎Ͱࣗ෼ͷΞϓϦͳΒ

  36. ςΩετҎ֎Ͱࣗ෼ͷΞϓϦͳΒ wBccessibilityIdentifierΛઃఆͯ͠ ["Accessibility Identifier"]͔ΒׂΓग़͢ • images["Accessibility Identifier"] • otherElements["Accessibility Identifier"]

  37. XCTContext.runActivity(named: "Search Elements") { (activity) -> Void in // app

    ͔ΒݟΕΔ͢΂ͯͷ otherElements Λ૸ࠪ app.otherElements.allElementsBoundByIndex.forEach { (element) in // ඞͣ element ͕ଘࡏ͠ɺ͔ͭαΠζ͕ zero ͡Όͳ͍͜ͱΛอূ guard element.exists, element.frame.size != .zero else { return } // ֘౰ element ͷεΫϦʔϯγϣοτΛࡱΔ let screenshot = element.screenshot() // εΫϦʔϯγϣοτ͔Βఴ෇Λ࡞Δ let attachment = XCTAttachment(screenshot: screenshot) // ݱࡏͷ activity ʹఴ෇Λ௥Ճ activity.add(attachment) } } ςΩετҎ֎ͰଞਓͷΞϓϦͳΒ      
  38. ςΩετҎ֎ͰଞਓͷΞϓϦͳΒ ֘౰ͦ͠͏ͳࢠཁૉͷछྨΛશ෦૸ࠪ͢Δ ͦΕΒͷཁૉΛ͢΂ͯεΫγϣΛࡱͬͯϨϙʔτʹఴ෇͢Δ Ϩϙʔτͷఴ෇εΫγϣը૾͔Β֘౰͢ΔཁૉΛ୳͠ग़͠ɺ ఴ෇໊͔Βཁૉ໊Λಛఆ͢Δ

  39. XCTContext.runActivity(named: "Search Elements") { activity -> Void in let shareList

    = app.otherElements["ActivityListView"] XCTAssert(shareList.waitForExistence(timeout: 2)) // ڞ༗ϝχϡʔ ͔ΒݟΕΔ͢΂ͯͷ cell Λ૸ࠪ shareList.cells.allElementsBoundByIndex.forEach { (cell) in guard cell.exists, cell.frame.size != .zero else { return } let screenshot = cell.screenshot() let attachment = XCTAttachment(screenshot: screenshot) activity.add(attachment) } } ଞਓͷΞϓϦͳΒ
  40. ݱஈ֊ɺڞ༗ϝχϡʔ͔Β ਖ਼֬ʹࣗ෼ͷΞϓϦϘλϯΛ ׂΓग़͢͜ͱ͸ෆՄೳ ˞ࢀߟɿIUUQTHJTUHJUIVCDPN"WE-FFCEFEGDDBDBEHJTUDPNNFOU

  41. // ڞ༗ϝχϡʔΛ୳͢ let shareList = app.otherElements["ActivityListView"] // ڞ༗ϝχϡʔ͕දࣔ͞ΕΔ·Ͱ࠷େ 2 ඵ଴ͭ

    XCTAssert(shareList.waitForExistence(timeout: 2)) // ͦͷڞ༗ϝχϡʔ͔Β֘౰ΞϓϦͷΞΠίϯΛ୳͢ let cell = shareList.cells.matching(identifier: "Activity") .allElementsBoundByIndex[1] // ͦͷΞΠίϯΛԡ͢ cell.tap() ͳΜ͡Ό͜Εʂʁ NBUDIJOHʁ "DUJWJUZʁ <>ʁ
  42. // ͢΂ͯͷηϧ͔Β૸ࠪ let cell = shareList.cells // `Activity` ͷηϧΛநग़ .matching(identifier:

    "Activity") // ͦͷ 2 ൪໨ͷηϧ .allElementsBoundByIndex[1] ˞࣮ߦ͞ΕΔ୺຤ͷڞ༗ઃఆʹґଘ͢ΔͨΊ$*Ͱอূͤ͞Δ͜ͱ͸ݱࡏෆՄೳ
  43. ·ͱΊ w 6*5FTU͸ΞϓϦʹରͯ͠௚઀ૢ࡞Λߦ͏ςετ w 6*5FTUͰ4IBSF&YUFOTJPOରԠͷڞ༗ը໘΋ςετՄೳ w εΫγϣΛࡱͬͯ֬ೝ͢Δ͜ͱͰ6*5FTUͰ࢖͑Δཁૉ໊͕Θ͔Δ w ڞ༗ϝχϡʔࣗମ͸ݱஈ֊ສશͰਖ਼֬ͳςετίʔυ͕ॻ͚ͳ͍ ࢀߟɿIUUQTHJUIVCDPNFMIPTIJOP2VJDLTIB3FCMPCDFGDGGEBEEDFCDFE2VJDLTIB3F6*5FTUT2VJDLTIB3F6*5FTUTTXJGU

  44. ʲએ఻ʳ

  45. 2VJDLTIB3Fɺઈࢍ഑৴தʂ IUUQTBQQTBQQMFDPNKQBQQRVJDLTIBSFTIBSFWJBRSDPEFJE

  46. גࣜձࣾΏΊΈɺΤϯδχΞઈࢍืूத IUUQSFDSVJUZVNFNJDPKQ 䱰⾂ ؽ٨ضٌ ✌؜猳