| final class SpotsUITests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
// Only launch once — reuse the running instance for subsequent tests
if app.state != .runningForeground {
app.launch()
}
// Dismiss any sheet left open by the previous test
let cancel = app.navigationBars.buttons["Cancel"]
if cancel.exists { cancel.tap() }
}
override func tearDownWithError() throws {
// Don't terminate — keep the app alive for the next test
app = nil
}
// MARK: - Tab Bar
func testTabBarHasFiveTabs() throws {
let tabBar = app.tabBars.firstMatch
XCTAssertTrue(tabBar.exists, "Tab bar should exist")
XCTAssertEqual(tabBar.buttons.count, 5, "Tab bar should have exactly 5 tabs")
}
func testAllExpectedTabsExist() throws {
let tabBar = app.tabBars.firstMatch
XCTAssertTrue(tabBar.buttons["Map"].exists, "Map tab should exist")
XCTAssertTrue(tabBar.buttons["Shops"].exists, "Shops tab should exist")
XCTAssertTrue(tabBar.buttons["Sites"].exists, "Sites tab should exist")
XCTAssertTrue(tabBar.buttons["Shows"].exists, "Shows tab should exist")
XCTAssertTrue(tabBar.buttons["Food"].exists, "Food tab should exist")
}
func testNoMoreButtonInTabBar() throws {
XCTAssertFalse(app.tabBars.firstMatch.buttons["More"].exists,
"Tab bar should not have a 'More' button")
}
func testTabSwitching() throws {
let tabBar = app.tabBars.firstMatch
tabBar.buttons["Food"].tap()
XCTAssertTrue(app.navigationBars["Food"].exists || app.staticTexts["Food"].exists,
"Food tab content should be visible")
tabBar.buttons["Shows"].tap()
XCTAssertTrue(app.navigationBars["Shows"].exists || app.staticTexts["Shows"].exists,
"Shows tab content should be visible")
tabBar.buttons["Shops"].tap()
XCTAssertTrue(app.navigationBars["Shops"].exists || app.staticTexts["Shops"].exists,
"Shops tab content should be visible")
tabBar.buttons["Sites"].tap()
XCTAssertTrue(app.navigationBars["Sites"].exists || app.staticTexts["Sites"].exists,
"Sites tab content should be visible")
}
// MARK: - Search
func testSearchBarExistsOnFoodTab() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
// The app uses a TextField (not a system SearchBar) for filtering
let searchField = app.textFields.firstMatch
XCTAssertTrue(searchField.exists, "Search field should exist on Food tab")
}
func testSearchBarExistsOnShowsTab() throws {
app.tabBars.firstMatch.buttons["Shows"].tap()
let searchField = app.textFields.firstMatch
XCTAssertTrue(searchField.exists, "Search field should exist on Shows tab")
}
func testSearchBarExistsOnShopsTab() throws {
app.tabBars.firstMatch.buttons["Shops"].tap()
let searchField = app.textFields.firstMatch
XCTAssertTrue(searchField.exists, "Search field should exist on Shops tab")
}
func testTypingInSearchFilters() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let searchField = app.textFields.firstMatch
guard searchField.exists else { return }
searchField.tap()
searchField.typeText("pizza")
XCTAssertEqual(searchField.value as? String, "pizza",
"Search field should contain typed text")
}
// MARK: - Add Entry Form
func testAddEntryButtonExistsOnFoodTab() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
XCTAssertTrue(addButton.exists, "Add entry button should exist on Food tab")
}
func testAddFoodEntryFormOpens() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
XCTAssertTrue(
app.navigationBars["Add"].exists || app.staticTexts["Add"].exists,
"Add entry form should open"
)
}
func testAddFoodEntryFormHasNameField() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
XCTAssertTrue(app.textFields["Name"].exists, "Name field should exist in Add form")
}
func testAddFoodEntryRequiresName() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
let saveButton = app.navigationBars.buttons["Save"]
if saveButton.exists {
XCTAssertFalse(saveButton.isEnabled, "Save button should be disabled when name is empty")
}
}
func testAddFoodEntryCanBeSaved() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
let nameField = app.textFields["Name"]
guard nameField.exists else { XCTFail("Name field not found"); return }
nameField.tap()
nameField.typeText("Test Restaurant")
let saveButton = app.navigationBars.buttons["Save"]
XCTAssertTrue(saveButton.isEnabled, "Save button should be enabled when name is filled")
saveButton.tap()
XCTAssertFalse(app.navigationBars["Add"].exists, "Add form should dismiss after saving")
}
func testCancelAddEntryForm() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
XCTAssertTrue(app.navigationBars["Add"].exists, "Add form should be open")
let cancelButton = app.navigationBars.buttons["Cancel"]
XCTAssertTrue(cancelButton.exists, "Cancel button should exist")
cancelButton.tap()
XCTAssertFalse(app.navigationBars["Add"].exists, "Add form should dismiss after cancel")
}
func testAddShowEntryFormOpens() throws {
app.tabBars.firstMatch.buttons["Shows"].tap()
let addButton = app.navigationBars.buttons.matching(
NSPredicate(format: "label CONTAINS 'Add' OR label == '+'")
).firstMatch
guard addButton.exists else { return }
addButton.tap()
XCTAssertTrue(
app.navigationBars["Add"].exists || app.staticTexts["Add"].exists,
"Add form should open from Shows tab"
)
XCTAssertTrue(
app.staticTexts["Show Information"].exists ||
app.staticTexts["SHOW INFORMATION"].exists,
"Add form from Shows tab should show Show Information section"
)
}
// MARK: - Map Tab
func testMapTabOpens() throws {
app.tabBars.firstMatch.buttons["Map"].tap()
XCTAssertTrue(app.tabBars.firstMatch.buttons["Map"].isSelected,
"Map tab should be selected after tapping")
}
// MARK: - Empty State
func testFoodTabOpens() throws {
app.tabBars.firstMatch.buttons["Food"].tap()
XCTAssertTrue(app.tabBars.firstMatch.buttons["Food"].isSelected,
"Food tab should be selected")
}
func testShowsTabOpens() throws {
app.tabBars.firstMatch.buttons["Shows"].tap()
XCTAssertTrue(app.tabBars.firstMatch.buttons["Shows"].isSelected,
"Shows tab should be selected")
}
func testSitesTabOpens() throws {
app.tabBars.firstMatch.buttons["Sites"].tap()
XCTAssertTrue(app.tabBars.firstMatch.buttons["Sites"].isSelected,
"Sites tab should be selected")
}
func testShopsTabOpens() throws {
app.tabBars.firstMatch.buttons["Shops"].tap()
XCTAssertTrue(app.tabBars.firstMatch.buttons["Shops"].isSelected,
"Shops tab should be selected")
}
} | `SpotsUITests` class | Defines the `SpotsUITests` class. Conforms to XCTestCase. |