diff --git a/VoiceOver Designer.xcodeproj/project.pbxproj b/VoiceOver Designer.xcodeproj/project.pbxproj index fabf10d2..caf3c6da 100644 --- a/VoiceOver Designer.xcodeproj/project.pbxproj +++ b/VoiceOver Designer.xcodeproj/project.pbxproj @@ -41,11 +41,14 @@ 1ECE12DF2969B91700A3E917 /* WindowRestorationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ECE12DE2969B91700A3E917 /* WindowRestorationTests.swift */; }; 1EEE25C32944ADC900B84F01 /* Navigator in Frameworks */ = {isa = PBXBuildFile; productRef = 1EEE25C22944ADC900B84F01 /* Navigator */; }; 236A3C182AC756B200156F00 /* Presentation in Frameworks */ = {isa = PBXBuildFile; productRef = 236A3C172AC756B200156F00 /* Presentation */; }; + 589562AC2C19AA770086BC3F /* CanvasTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 589562AB2C19AA770086BC3F /* CanvasTests.swift */; }; 742D242A2ADD939200EC224A /* RecentWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 742D24292ADD939200EC224A /* RecentWindowController.swift */; }; 742D242C2AE0BF9800EC224A /* ProjectStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 742D242B2AE0BF9800EC224A /* ProjectStateController.swift */; }; 7439CD4429C664A800A557FC /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7439CD4329C664A800A557FC /* StoreKit.framework */; }; 74866ADE2AE6DFBA00D42AFA /* PresentationToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74866ADD2AE6DFBA00D42AFA /* PresentationToolbar.swift */; }; 74866AE12AE6E1CF00D42AFA /* ToolbarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74866AE02AE6E1CF00D42AFA /* ToolbarItems.swift */; }; + 74E0D62F2C19BD9A008E1E77 /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E0D62E2C19BD9A008E1E77 /* StatusBar.swift */; }; + 74E0D6312C19C32A008E1E77 /* Navigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E0D6302C19C32A008E1E77 /* Navigator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -116,6 +119,7 @@ 1EC992DA28B119730020041B /* ProjectStateController+Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProjectStateController+Toolbar.swift"; sourceTree = ""; }; 1ECE12DE2969B91700A3E917 /* WindowRestorationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowRestorationTests.swift; sourceTree = ""; }; 1EF217D4281EBBE00036B09B /* VoiceOver-Designer-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "VoiceOver-Designer-Info.plist"; sourceTree = SOURCE_ROOT; }; + 589562AB2C19AA770086BC3F /* CanvasTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CanvasTests.swift; sourceTree = ""; }; 6B4705F729048D9800AC4C23 /* VoiceOver DesignerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "VoiceOver DesignerRelease.entitlements"; sourceTree = ""; }; 6B628795288D3A8D006F622A /* Features */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Features; path = "VoiceOver Designer/Features"; sourceTree = ""; }; 742D24292ADD939200EC224A /* RecentWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentWindowController.swift; sourceTree = ""; }; @@ -123,6 +127,8 @@ 7439CD4329C664A800A557FC /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 74866ADD2AE6DFBA00D42AFA /* PresentationToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationToolbar.swift; sourceTree = ""; }; 74866AE02AE6E1CF00D42AFA /* ToolbarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarItems.swift; sourceTree = ""; }; + 74E0D62E2C19BD9A008E1E77 /* StatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = ""; }; + 74E0D6302C19C32A008E1E77 /* Navigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -226,7 +232,9 @@ isa = PBXGroup; children = ( 1E7B569E28B37BE0008D1871 /* Settings.swift */, + 74E0D62E2C19BD9A008E1E77 /* StatusBar.swift */, 1E7B56A928B38042008D1871 /* Canvas.swift */, + 74E0D6302C19C32A008E1E77 /* Navigator.swift */, 1E7B56A028B37BEF008D1871 /* TextSummary.swift */, ); path = Panels; @@ -301,6 +309,7 @@ 1ECE12DE2969B91700A3E917 /* WindowRestorationTests.swift */, 1E183AE528B50600008B0F07 /* DocumentFromFileTests.swift */, 1E7B56AE28B38F5D008D1871 /* AdjustableTest.swift */, + 589562AB2C19AA770086BC3F /* CanvasTests.swift */, ); path = Tests; sourceTree = ""; @@ -474,8 +483,10 @@ buildActionMask = 2147483647; files = ( 1EB554B228B274B6000DC4FB /* Robot.swift in Sources */, + 74E0D6312C19C32A008E1E77 /* Navigator.swift in Sources */, 1E7B56A728B37C51008D1871 /* RecentWindow.swift in Sources */, 1E183AE628B50600008B0F07 /* DocumentFromFileTests.swift in Sources */, + 74E0D62F2C19BD9A008E1E77 /* StatusBar.swift in Sources */, 1E7B569C28B37B7C008D1871 /* DesignerTests.swift in Sources */, 1ECE12DF2969B91700A3E917 /* WindowRestorationTests.swift in Sources */, 1E7B569F28B37BE0008D1871 /* Settings.swift in Sources */, @@ -485,6 +496,7 @@ 1E7B56AA28B38042008D1871 /* Canvas.swift in Sources */, 1E7B56A128B37BEF008D1871 /* TextSummary.swift in Sources */, 1E7B56A328B37C07008D1871 /* ProjectPanel.swift in Sources */, + 589562AC2C19AA770086BC3F /* CanvasTests.swift in Sources */, 1EA7411928B2683000D6CAA4 /* SettingsTests.swift in Sources */, 1E80528728B4DF6300743023 /* WindowTests.swift in Sources */, ); @@ -630,7 +642,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 28; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "VoiceOver-Designer-Info.plist"; @@ -661,7 +673,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 28; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "VoiceOver-Designer-Info.plist"; @@ -687,7 +699,7 @@ CODE_SIGN_ENTITLEMENTS = MacOSPreview/MacOSPreview.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MacOSPreview/Info.plist; @@ -714,7 +726,7 @@ CODE_SIGN_ENTITLEMENTS = MacOSPreview/MacOSPreview.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MacOSPreview/Info.plist; @@ -804,7 +816,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 28; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "VoiceOver-Designer-Info.plist"; @@ -830,7 +842,7 @@ CODE_SIGN_ENTITLEMENTS = MacOSPreview/MacOSPreview.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = P67Q4Q7HA9; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MacOSPreview/Info.plist; diff --git a/VoiceOver Preview/VoiceOver Preview.xcodeproj/xcshareddata/xcschemes/DesignerUITests.xcscheme b/VoiceOver Preview/VoiceOver Preview.xcodeproj/xcshareddata/xcschemes/DesignerUITests.xcscheme index 4bf8ca48..c8c7d7c4 100644 --- a/VoiceOver Preview/VoiceOver Preview.xcodeproj/xcshareddata/xcschemes/DesignerUITests.xcscheme +++ b/VoiceOver Preview/VoiceOver Preview.xcodeproj/xcshareddata/xcschemes/DesignerUITests.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + language = "en"> diff --git a/VoiceOverDesignerUITests/Base/DesignerTests.swift b/VoiceOverDesignerUITests/Base/DesignerTests.swift index c6302289..7f06fc06 100644 --- a/VoiceOverDesignerUITests/Base/DesignerTests.swift +++ b/VoiceOverDesignerUITests/Base/DesignerTests.swift @@ -4,6 +4,7 @@ class DesignerTests: XCTestCase { var app: XCUIApplication! override func setUpWithError() throws { + try super.setUpWithError() continueAfterFailure = false app = XCUIApplication() @@ -60,6 +61,16 @@ class DesignerTests: XCTestCase { project.settingsPanel } + var statusBar: StatusBar { + StatusBar(window: project.projectWindow, + app: app) + } + + var navigator: Navigator { + Navigator(window: project.projectWindow, + app: app) + } + func closeWindow() { app.windows.firstMatch.buttons[XCUIIdentifierCloseWindow].click() } diff --git a/VoiceOverDesignerUITests/PageObjects/Panels/Canvas.swift b/VoiceOverDesignerUITests/PageObjects/Panels/Canvas.swift index ebeabec2..04665e22 100644 --- a/VoiceOverDesignerUITests/PageObjects/Panels/Canvas.swift +++ b/VoiceOverDesignerUITests/PageObjects/Panels/Canvas.swift @@ -38,4 +38,59 @@ class Canvas: ProjectPanel { tap(location) return self } + + @discardableResult + func click(dx: Double, dy: Double) -> Self { + let window = app.windows.firstMatch + let assertClick = CGVector(dx: dx, dy: dy) + let tapTest = window.coordinate(withNormalizedOffset: assertClick) + tapTest.press(forDuration: 0.01) + + return self + } + + @discardableResult + func deselect(dx: Double, dy: Double) -> Self { + click(dx: dx, dy: dy) + XCTAssertTrue(app.staticTexts["Select or draw a control\nto adjust settings"].exists) + + return self + } + + @discardableResult + func select(dx: Double, dy: Double) -> Self { + click(dx: dx, dy: dy) + XCTAssertFalse(app.staticTexts["Select or draw a control\nto adjust settings"].exists) + + return self + } + + @discardableResult + func drag(from: Double, to: Double) -> Self { + let from = CGVector(dx: from, dy: from) + let to = CGVector(dx: to, dy: to) + + let window = app.windows.firstMatch + let start = window.coordinate(withNormalizedOffset: from) + let finish = window.coordinate(withNormalizedOffset: to) + + start.press(forDuration: 0.01, thenDragTo: finish) + + return self + } + + @discardableResult + func assertNoElements() -> Self { + let assertNoElementsCanvas: () = XCTAssertTrue(app.staticTexts["Add your screenshot"].exists) + + return self + } + + @discardableResult + func assertHaveElements() -> Self { + let assertHaveElementsCanvas: () = XCTAssertFalse(app.staticTexts["Add your screenshot"].exists) + + return self + } + } diff --git a/VoiceOverDesignerUITests/PageObjects/Panels/Navigator.swift b/VoiceOverDesignerUITests/PageObjects/Panels/Navigator.swift new file mode 100644 index 00000000..f9861da6 --- /dev/null +++ b/VoiceOverDesignerUITests/PageObjects/Panels/Navigator.swift @@ -0,0 +1,37 @@ +import XCTest + +class Navigator: ProjectPanel { + + var navigator: XCUIElement { window.outlines.firstMatch } + + @discardableResult + func groupInContainer() -> Self { + app.buttons["Group in Container"].click() + + return self + } + + @discardableResult + func assertFirstCell( + text: String, + file: StaticString = #file, + line: UInt = #line + ) -> Self { + let actualText = navigator.cells.staticTexts.firstMatch.value as? String + + XCTAssertEqual(actualText, text, file: file, line: line) + return self + } + + @discardableResult + func assertElementsCount( + count: Int, + file: StaticString = #file, + line: UInt = #line + ) -> Self { + let cellsCount = navigator.cells.count + + XCTAssertEqual(cellsCount, count, file: file, line: line) + return self + } +} diff --git a/VoiceOverDesignerUITests/PageObjects/Panels/Settings.swift b/VoiceOverDesignerUITests/PageObjects/Panels/Settings.swift index 6a3aa1bc..60969802 100644 --- a/VoiceOverDesignerUITests/PageObjects/Panels/Settings.swift +++ b/VoiceOverDesignerUITests/PageObjects/Panels/Settings.swift @@ -165,6 +165,11 @@ class Settings: ProjectPanel { func customDescriptionValue() -> String? { return customDescriptionValueField.value as? String } + + func clickButtonTrait() { + window.checkBoxes["Button"].click() + } + } extension XCUIElement { @@ -178,6 +183,9 @@ extension XCUIElement { } func inputEnter() { - typeText("\r") + typeKey(XCUIKeyboardKey.enter, modifierFlags: []) + } + func inputDelete() { + typeKey(XCUIKeyboardKey.delete, modifierFlags: []) } } diff --git a/VoiceOverDesignerUITests/PageObjects/Panels/StatusBar.swift b/VoiceOverDesignerUITests/PageObjects/Panels/StatusBar.swift new file mode 100644 index 00000000..394d40d5 --- /dev/null +++ b/VoiceOverDesignerUITests/PageObjects/Panels/StatusBar.swift @@ -0,0 +1,81 @@ +// +// StatusBar.swift +// VoiceOverDesignerUITests +// +// Created by Mikhail Rubanov on 12.06.2024. +// + +import XCTest + +class StatusBar: ProjectPanel { + + var statusBar: XCUIElement { app.menuBars.firstMatch } + + var fileMenu: XCUIElement {statusBar.menuBarItems["File"].firstMatch} + + var editMenu: XCUIElement { statusBar.menuBarItems["Edit"].firstMatch } + + var fileNewMenu: XCUIElement {statusBar.menuItems["New"].firstMatch} + + var selectAllMenu: XCUIElement { statusBar.menuItems["Select All"].firstMatch } + + var undoMenu: XCUIElement {statusBar.menuItems["Undo"].firstMatch} + + var redoMenu: XCUIElement {statusBar.menuItems["Redo"].firstMatch} + + @discardableResult + func clickFile() -> Self { + fileMenu.click() + + return self + } + + @discardableResult + func clickFileNew() -> Self { + fileNewMenu.click() + + return self + } + + @discardableResult + func clickEdit() -> Self { + editMenu.click() + + return self + } + + @discardableResult + func clickSelectAll() -> Self { + selectAllMenu.click() + + return self + } + + @discardableResult + func clickUndoMenu() -> Self { + undoMenu.click() + + return self + } + + @discardableResult + func clickRedoMenu() -> Self { + redoMenu.click() + + return self + } + + @discardableResult + func openInFullScreen() -> Self { + app.buttons[XCUIIdentifierFullScreenWindow].click() + + return self + } + + @discardableResult + func closeWindow() -> Self { + app.buttons[XCUIIdentifierCloseWindow].click() + + return self + } +} diff --git a/VoiceOverDesignerUITests/Tests/CanvasTests.swift b/VoiceOverDesignerUITests/Tests/CanvasTests.swift new file mode 100644 index 00000000..e91030af --- /dev/null +++ b/VoiceOverDesignerUITests/Tests/CanvasTests.swift @@ -0,0 +1,144 @@ +// +// SettingsTestsAuto.swift +// VoiceOverDesignerUITests +// +// Created by Mikhail Rubanov on 11.04.2024. +// + +import XCTest + +final class CanvasTests: DesignerTests { + + override func setUpWithError() throws { + try super.setUpWithError() + + app.launch() + + createNewFile() + drawRectangle() + } + + func testExample() { + settings + .inputLabel("City") + .clickButtonTrait() + + navigator + .assertFirstCell(text: "City. Button.") + } + + func testExample2() { + settings + .inputLabel("Country") + .clickButtonTrait() + + navigator + .assertFirstCell(text: "Country. Button.") + } + + func testDeleteUndoRedo() { + app.inputDelete() + canvas + .assertNoElements() + navigator + .assertElementsCount(count: 0) + + statusBar + .clickEdit() + .clickUndoMenu() + canvas + .assertHaveElements() + navigator + .assertElementsCount(count: 1) + /*statusBar + .clickEdit() + .clickRedoMenu() + canvas + .assertNoElements()*/ + + + } + + func testDragUndoRedo() { + canvas + .drag(from: 0.45, to: 0.5) + .deselect(dx: 0.4, dy: 0.4) + + statusBar + .clickEdit() + .clickUndoMenu() + canvas + .select(dx: 0.41, dy: 0.41) + + statusBar + .clickEdit() + .clickRedoMenu() + canvas + .select(dx: 0.55, dy: 0.55) + + } + + func testCreateOneElementContainer() { + //Разворачиваем приложение на весь экран + statusBar + .openInFullScreen() + navigator + .groupInContainer() + .assertFirstCell(text: "Container") + } + + func testCreateTwoElementContainer() { + canvas + .draw(from: CGVector(dx: 0.4, dy: 0.6), + to: CGVector(dx: 0.5, dy: 0.7)) + + statusBar + .clickEdit() + .clickSelectAll() + + statusBar + .openInFullScreen() // TODO: Reduce window's size + + navigator + .groupInContainer() + .assertFirstCell(text: "Container") + } + + func testChangeSize() { + canvas + .deselect(dx: 0.55, dy: 0.55) // same as assert + + canvas + .select(dx: 0.45, dy: 0.45) + .drag(from: 0.5, to: 0.6) + .deselect(dx: 0.61, dy: 0.61) + + canvas + .select(dx: 0.55, dy: 0.55) // same as arrange + } + + override func tearDown() { + closeWindowAndDelete() + + super.tearDown() + } + + private func createNewFile() { + // Создаем новый документ + statusBar + .clickFile() + .clickFileNew() + } + + func drawRectangle() { + canvas + .drag(from: 0.4, to: 0.5) + } + + func closeWindowAndDelete() { + statusBar + .closeWindow() + settings + .delete() + } +}