Platform-Specific Tests
spana runs a single flow file against multiple platforms. There are two mechanisms for platform-specific behavior: branching inside a flow, and restricting a flow to specific platforms entirely.
Branching inside a flow
Section titled “Branching inside a flow”The platform value in FlowContext is "web" | "android" | "ios". Use it for conditional logic within a flow that is mostly shared across platforms.
import { flow } from "spana-test";
export default flow("user can share content", async ({ app, expect, platform }) => { await app.tap({ testID: "share-button" });
if (platform === "ios") { // iOS share sheet uses a native action sheet await expect({ text: "Copy Link" }).toBeVisible(); await app.tap({ text: "Copy Link" }); } else if (platform === "android") { // Android uses the system share dialog await expect({ text: "Copy to clipboard" }).toBeVisible(); await app.tap({ text: "Copy to clipboard" }); } else { // Web uses a custom share modal await expect({ testID: "share-modal" }).toBeVisible(); await app.tap({ testID: "copy-link" }); }
await expect({ testID: "share-confirmation" }).toBeVisible();});Restricting a flow to specific platforms
Section titled “Restricting a flow to specific platforms”Use FlowConfig.platforms to prevent a flow from running on platforms where it does not apply.
import { flow } from "spana-test";
export default flow( "push notification permissions", { platforms: ["android", "ios"] }, async ({ app, expect }) => { await app.tap({ testID: "enable-notifications" }); await expect({ testID: "permission-granted" }).toBeVisible(); },);This flow is skipped entirely when the runner targets web.
CLI platform filtering
Section titled “CLI platform filtering”Regardless of per-flow platforms config, you can also filter at the CLI:
# Run only on webspana test --platform web
# Run on android and ios onlyspana test --platform android,iosCLI filtering takes effect before per-flow filtering. A flow with platforms: ["android"] will not run if you pass --platform web, even if both are in your config.
Common patterns
Section titled “Common patterns”Platform-specific selectors
Section titled “Platform-specific selectors”Some elements have the same semantic purpose but different identifiers across platforms. Assign the selector based on platform:
export default flow("open menu", async ({ app, platform }) => { const menuSelector = platform === "ios" ? { accessibilityLabel: "Main menu" } : { testID: "menu-button" };
await app.tap(menuSelector);});Skip web for native-only features
Section titled “Skip web for native-only features”export default flow( "biometric authentication", { platforms: ["ios", "android"] }, async ({ app, expect }) => { await app.tap({ testID: "use-biometrics" }); await expect({ testID: "biometric-success" }).toBeVisible({ timeout: 10000 }); },);Deep link only on mobile
Section titled “Deep link only on mobile”export default flow("deep link navigation", async ({ app, platform }) => { if (platform !== "web") { await app.openLink("myapp://profile/settings"); } else { await app.tap({ testID: "settings-nav" }); } await expect({ testID: "settings-screen" }).toBeVisible();});