Skip to content

Commit a130431

Browse files
author
Ajit Kumar
committed
fix(ci): complete FAQ translations and stabilize iOS tests
Add the missing FAQ error, empty-state, and retry keys to 32 locales using English fallbacks so translation validation passes. Align existing iOS tests with the current file-browser menus, page lifecycle, terminal storage, and standalone worker console. Improve fixture cleanup, wait for keyboard layout, verify persisted icon selection, and isolate gesture and theme tests from ads and startup updates. Validation: 160 iPhone tests and 30 iPad checks passed on iOS 18.6 simulators. Translation validation, formatting, and diff checks passed.
1 parent 1d95443 commit a130431

40 files changed

Lines changed: 197 additions & 68 deletions

‎platforms/ios/runnerTests/ArchiveTests.swift‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ final class ArchiveTests: BridgeTestCase {
3535
browser.querySelector('li[data-name="'+name+'"]').click();
3636
stage='select entries';
3737
await wait(()=>browser.querySelector('li[data-name="notes.txt"]'));
38-
page.querySelector('[data-action="toggle-selection-mode"]').click();
38+
page.querySelector('[data-action="toggle-menu"]').click();
39+
document.querySelector('.context-menu [action="select-items"]').click();
40+
await wait(()=>!document.querySelector('.context-menu'));
3941
browser.querySelector('li[data-name="日本語 # sample"]').click();
4042
browser.querySelector('li[data-name="notes.txt"]').click();
41-
page.querySelector('[data-action="toggle-selection-menu"]').click();
43+
page.querySelector('[data-action="toggle-menu"]').click();
4244
document.querySelector('.context-menu [action="compress"]').click();
4345
stage='read generated archive';
4446
let archive;
@@ -143,19 +145,23 @@ final class ArchiveTests: BridgeTestCase {
143145
}
144146

145147
private func cleanup(_ app: WKWebView) async throws {
146-
_ = try await app.evaluateJavaScript("""
148+
_ = try await app.callAsyncJavaScript("""
147149
const cancellation=window.archiveCancellation;
148150
if(cancellation){
149151
document.querySelector('#__loader button')?.click();
150152
Bridge.exec=cancellation.exec;cancellation.release?.();delete window.archiveCancellation;
151153
}
152-
document.getElementById('file-browser')?.closest('wc-page')?.querySelector('[data-action="close"]')?.click();
154+
const page=document.getElementById('file-browser')?.closest('wc-page');
155+
if(page?.querySelector('.selection-mode'))page.querySelector('[data-action="close"]').click();
156+
page?.querySelector('[data-action="close"]')?.click();
157+
for(let n=0;page?.isConnected&&n<100;n++)await new Promise(resolve=>setTimeout(resolve,20));
158+
if(page?.isConnected)throw Error('Archive browser did not close');
153159
const saved=window.archiveFixture;
154160
if(saved){
155161
if(saved.storage===undefined)delete localStorage.storageList;else localStorage.storageList=saved.storage;
156162
if(saved.state===undefined)delete localStorage.fileBrowserState;else localStorage.fileBrowserState=saved.state;
157163
}
158164
delete window.archiveFixture;
159-
""")
165+
""", arguments: [:], in: nil, contentWorld: .page)
160166
}
161167
}

‎platforms/ios/runnerTests/ConsoleObjectTests.swift‎

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import WebKit
33
@testable import runner
44

55
extension PreviewBrowserTests {
6-
func testConsoleInspectsWindowAndKeepsObjectPropertiesInline() async throws {
6+
func testStandaloneConsoleInspectsObjectsAndRecoversAfterCancellation() async throws {
77
let app = try await editorWebView()
88
var responder: UIResponder? = app
99
while responder != nil, !(responder is WebViewController) { responder = responder?.next }
@@ -28,9 +28,7 @@ extension PreviewBrowserTests {
2828
const nested=[...document.querySelectorAll('c-line')].find(row=>row.firstElementChild?.textContent==='nested:');
2929
nested.querySelector('c-type').click();
3030
const expanded=nested.textContent.includes('enabled:')&&nested.textContent.includes('true');
31-
const context=document.querySelector('.__c-context');
32-
const defaultContext=context.value;
33-
context.value='worker';
31+
const hasContextSelector=!!document.querySelector('.__c-context');
3432
input.value='while(true){}';
3533
input.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',bubbles:true,cancelable:true}));
3634
const stop=document.querySelector('[aria-label="Stop JavaScript execution"]');
@@ -39,26 +37,21 @@ extension PreviewBrowserTests {
3937
await wait(()=>!input.disabled);
4038
await submit('21*2');
4139
const workerRecovered=document.querySelector('c-message[log-level="log"] c-text')?.textContent==='42';
42-
context.value='page';
43-
await submit('window');
44-
const toggle=document.querySelector('c-message[log-level="log"] c-type');
45-
const type=toggle?.textContent;
46-
toggle?.click();
47-
const inspectable=[...document.querySelectorAll('c-key')].some(key=>key.textContent==='document:');
40+
await submit('typeof window');
41+
const windowType=document.querySelector('c-message[log-level="log"] c-text')?.textContent;
4842
const error=document.querySelector('c-message[log-level="error"]')?.textContent;
4943
input.blur();
50-
return {inline,expanded,error:error||'',type:type||'',inspectable,defaultContext,workerRecovered};
44+
return {inline,expanded,error:error||'',windowType,hasContextSelector,workerRecovered};
5145
""", arguments: [:], in: nil, contentWorld: .page) as? [String: Any]
5246
XCTAssertEqual(result?["inline"] as? Bool, true, "Property keys and values must share a line")
5347
XCTAssertEqual(result?["expanded"] as? Bool, true)
5448
XCTAssertEqual(result?["error"] as? String, "")
55-
XCTAssertEqual(result?["type"] as? String, "Window")
56-
XCTAssertEqual(result?["inspectable"] as? Bool, true)
57-
XCTAssertEqual(result?["defaultContext"] as? String, "page")
49+
XCTAssertEqual(result?["windowType"] as? String, "undefined")
50+
XCTAssertEqual(result?["hasContextSelector"] as? Bool, false)
5851
XCTAssertEqual(result?["workerRecovered"] as? Bool, true)
5952
try await Task.sleep(for: .milliseconds(300))
6053
_ = try await browser.webView.evaluateJavaScript("document.querySelector('c-output').scrollTop=0")
6154
try await Task.sleep(for: .milliseconds(200))
62-
attachScreenshot(browser, name: "console-window-object")
55+
attachScreenshot(browser, name: "console-worker-object")
6356
}
6457
}

‎platforms/ios/runnerTests/FilesBrowserTests.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ final class FilesBrowserTests: BridgeTestCase {
5454
let value = try XCTUnwrap(result as? [String: Any])
5555
let entries = try XCTUnwrap(value["entries"] as? [String])
5656
XCTAssertTrue(entries.contains("ios-documents"))
57-
XCTAssertFalse(entries.contains("terminal-public"))
57+
XCTAssertTrue(entries.contains("terminal-public"))
5858
XCTAssertFalse(entries.contains("internal-storage"))
5959
XCTAssertEqual(value["name"] as? String, file.lastPathComponent)
6060
XCTAssertEqual(value["text"] as? String, "Files browser ✓")

‎platforms/ios/runnerTests/PlatformUITests.swift‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ final class PlatformUITests: BridgeTestCase {
2020
await wait('initial scroll',()=>scroller.scrollTop>0);
2121
const scrollTop=scroller.scrollTop;
2222
document.body.append(page);
23-
await wait('root detached',()=>!root.isConnected);
24-
if(document.querySelectorAll('wc-page').length!==1)throw Error('Covered editor remains mounted');
25-
if(!root.handler.$replacement.isConnected)throw Error('Editor lost its placeholder');
23+
await wait('cover shown',()=>page.isConnected&&getComputedStyle(page).opacity==='1');
24+
if(!root.isConnected||root.handler.$replacement.isConnected)throw Error('Primary editor was detached');
2625
page.hide();
2726
await wait('root restored',()=>root.isConnected&&!page.isConnected);
2827
await wait('scroll position restored',()=>scroller.scrollTop===scrollTop);
@@ -90,7 +89,7 @@ final class PlatformUITests: BridgeTestCase {
9089
system.openInBrowser=(...args)=>opened.push(args);
9190
Bridge.clipboard.copy=value=>{copied=value;};
9291
const stack=acode.require('actionStack');
93-
let settingsPage;
92+
let settingsPage,cover;
9493
try {
9594
await acode.exec('open','settings');
9695
await wait(()=>document.querySelector('.main-settings-page [data-key="about"]'));
@@ -108,8 +107,13 @@ final class PlatformUITests: BridgeTestCase {
108107
const scrollTop=body.scrollTop;
109108
document.querySelector('.main-settings-page [data-key="about"]').click();
110109
await wait(()=>document.querySelector('#about-page')?.textContent.includes('com.apple.WebKit'));
110+
if(!settingsElement.isConnected)throw Error('Previous page was detached too early');
111+
cover=acode.require('page')('Nested navigation fixture');
112+
document.body.append(cover);
111113
await wait(()=>!settingsElement.isConnected);
112-
if(!settingsElement.handler.$replacement.isConnected)throw Error('Covered settings page lost its placeholder');
114+
if(!settingsElement.handler.$replacement.isConnected)throw Error('Deeply covered settings page lost its placeholder');
115+
cover.hide();
116+
await wait(()=>settingsElement.isConnected&&!cover.isConnected);
113117
const info=document.querySelector('#about-page .info-item');
114118
if(info.hasAttribute('href')||!info.textContent.includes('WebKit'))throw Error('WebKit information offers an Android update link');
115119
info.click();
@@ -125,6 +129,7 @@ final class PlatformUITests: BridgeTestCase {
125129
return true;
126130
} finally {
127131
system.openInBrowser=openBrowser;Bridge.clipboard.copy=copy;
132+
if(cover?.isConnected){cover.hide();await wait(()=>!cover.isConnected);}
128133
if(document.getElementById('about-page'))stack.pop();
129134
settingsPage?.hide();
130135
}

‎platforms/ios/runnerTests/PreviewBrowserTests.swift‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class PreviewBrowserTests: BridgeTestCase {
1616
XCTAssertEqual(browser.address.text, "Console")
1717
XCTAssertNil(browser.menuButton.superview)
1818
assertCompactHeader(browser, screenshot: "console")
19+
await waitForKeyboardToHide(browser)
1920
let initialHeight = browser.content.bounds.height
2021
let result = try await browser.webView.callAsyncJavaScript("""
2122
const input=document.querySelector('#__c-input');
@@ -31,7 +32,7 @@ final class PreviewBrowserTests: BridgeTestCase {
3132
XCTAssertLessThan(browser.content.bounds.height, initialHeight, "The software keyboard must actually appear")
3233
attachScreenshot(browser, name: "console-keyboard")
3334
_ = try await browser.webView.evaluateJavaScript("document.querySelector('#__c-input').blur()")
34-
try await Task.sleep(for: .milliseconds(500))
35+
await waitForKeyboardToHide(browser)
3536
XCTAssertEqual(browser.content.bounds.height, initialHeight, accuracy: 1)
3637
await withCheckedContinuation { continuation in browser.dismiss(animated: false) { continuation.resume() } }
3738
} catch {
@@ -148,6 +149,15 @@ final class PreviewBrowserTests: BridgeTestCase {
148149
throw NSError(domain: "AcodeTests", code: 3, userInfo: [NSLocalizedDescriptionKey: "Preview failed: \(expression); \(String(describing: contents))"])
149150
}
150151

152+
private func waitForKeyboardToHide(_ browser: PreviewViewController) async {
153+
let hidden = XCTNSPredicateExpectation(predicate: NSPredicate { _, _ in
154+
browser.view.layoutIfNeeded()
155+
return browser.content.bounds.height > 0 &&
156+
abs(browser.content.frame.maxY - browser.view.safeAreaLayoutGuide.layoutFrame.maxY) < 1
157+
}, object: nil)
158+
await fulfillment(of: [hidden], timeout: 5)
159+
}
160+
151161
private func cleanup(_ app: WKWebView, port: Int) async throws {
152162
_ = try await app.callAsyncJavaScript("""
153163
const state=window.previewTestState;

‎platforms/ios/runnerTests/SystemUITests.swift‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,25 @@ final class SystemUITests: BridgeTestCase {
118118
var responder: UIResponder? = webView
119119
while responder != nil, !(responder is WebViewController) { responder = responder?.next }
120120
let controller = try XCTUnwrap(responder as? WebViewController)
121+
let color = controller.view.backgroundColor
122+
let type = controller.themeType
123+
// The startup theme timer must not overwrite direct bridge color checks.
124+
_ = try await webView.evaluateJavaScript("window.systemThemeFixture=system.setUiTheme;system.setUiTheme=()=>{};true")
125+
addTeardownBlock { @MainActor in
126+
_ = try await webView.evaluateJavaScript("system.setUiTheme=window.systemThemeFixture;delete window.systemThemeFixture")
127+
if let color { controller.setSystemBarColor(color) }
128+
controller.setThemeType(type)
129+
}
121130

122131
let theme: [String: Any] = ["name": "Fixture", "type": "dark", "version": "free"]
123132
try await call(webView, action: "set-ui-theme", args: ["#f8f9fa", theme])
124-
try await Task.sleep(for: .milliseconds(100))
125133
XCTAssertEqual(controller.view.backgroundColor, UIColor(hexString: "#f8f9fa"))
126134

127135
// Dialog darkening sends the darkened color as the first argument.
128136
try await call(webView, action: "set-ui-theme", args: ["#101010", theme])
129-
try await Task.sleep(for: .milliseconds(100))
130137
XCTAssertEqual(controller.view.backgroundColor, UIColor(hexString: "#101010"))
131138

132139
try await call(webView, service: "SystemBarPlugin", action: "setStatusBarBackgroundColor", args: [1, 2, 3])
133-
try await Task.sleep(for: .milliseconds(100))
134140
XCTAssertEqual(controller.view.backgroundColor, UIColor(red: 1.0 / 255, green: 2.0 / 255, blue: 3.0 / 255, alpha: 1))
135141
}
136142

‎platforms/ios/runnerUITests/AppIconUITests.swift‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class AppIconUITests: XCTestCase {
1919
let app = XCUIApplication()
2020
app.launchArguments = ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
2121
app.launch()
22-
dismissIconNotice(app, required: false)
22+
dismissIconNotice(app)
2323
let entry = app.webViews.staticTexts["App icon"].firstMatch
2424
if !entry.waitForExistence(timeout: 2) {
2525
let settings = app.webViews.staticTexts["Settings"].firstMatch
@@ -49,8 +49,13 @@ final class AppIconUITests: XCTestCase {
4949
continue
5050
}
5151
tapWhenReady(button)
52-
dismissIconNotice(app, required: true)
52+
dismissIconNotice(app)
5353
XCTAssertTrue(app.wait(for: .runningForeground, timeout: 5))
54+
tapWhenReady(entry)
55+
let selected = app.webViews.switches[icon].firstMatch
56+
let saved = XCTNSPredicateExpectation(predicate: NSPredicate(format: "value == %@", "1"), object: selected)
57+
await fulfillment(of: [saved], timeout: 5)
58+
tapWhenReady(app.webViews.buttons.matching(NSPredicate(format: "label ==[c] %@", "close")).firstMatch)
5459
}
5560
XCTAssertTrue(app.webViews.staticTexts["App icon"].firstMatch.exists)
5661
}
@@ -72,11 +77,10 @@ final class AppIconUITests: XCTestCase {
7277
}
7378
}
7479

75-
private func dismissIconNotice(_ app: XCUIApplication, required: Bool) {
80+
private func dismissIconNotice(_ app: XCUIApplication) {
7681
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
77-
let alert = app.alerts.firstMatch.waitForExistence(timeout: required ? 5 : 1)
82+
let alert = app.alerts.firstMatch.waitForExistence(timeout: 2)
7883
? app.alerts.firstMatch : springboard.alerts.firstMatch
79-
if required { XCTAssertTrue(alert.waitForExistence(timeout: 5), app.debugDescription) }
8084
if alert.exists {
8185
XCTAssertTrue(alert.staticTexts.matching(NSPredicate(format: "label CONTAINS %@", "changed the icon")).firstMatch.exists)
8286
alert.buttons["OK"].tap()

‎platforms/ios/runnerUITests/WebContextMenuUITests.swift‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import XCTest
2+
import StoreKitTest
23

34
@MainActor
45
final class WebContextMenuUITests: XCTestCase {
5-
func testHoldingAFileTabOpensItsExistingMenuOnRelease() {
6+
func testHoldingAFileTabOpensItsExistingMenuOnRelease() async throws {
67
continueAfterFailure = false
8+
// Keep this gesture test independent of network consent forms and ads.
9+
let configuration = try XCTUnwrap(Bundle(for: Self.self).url(forResource: "Iap", withExtension: "storekit"))
10+
let purchases = try SKTestSession(contentsOf: configuration)
11+
purchases.resetToDefaultState()
12+
purchases.clearTransactions()
13+
purchases.disableDialogs = true
14+
defer { purchases.clearTransactions(); purchases.resetToDefaultState() }
15+
_ = try await purchases.buyProduct(identifier: "acode_pro_new")
716
let app = XCUIApplication()
817
app.launchArguments = ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
918
app.launch()

‎src/lang/ar-ye.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,5 +889,8 @@
889889
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
890890
"icon pack": "Icon pack",
891891
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
892-
"unavailable": "unavailable"
892+
"unavailable": "unavailable",
893+
"faqs load failed": "Unable to load FAQs. Please try again.",
894+
"faqs empty": "No FAQs are available yet.",
895+
"retry": "Retry"
893896
}

‎src/lang/az-az.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,5 +889,8 @@
889889
"plugin-review:error-max-length": "Rəy ən çox {max} simvoldan ibarət olmalıdır.",
890890
"plugin-review:error-downvote-comment": "Nəyin yaxşılaşdırıla biləcəyini tərtibatçıya bildir.",
891891
"plugin-review:error-empty": "Şərh əlavə et və ya «Bəyən» seçimini et.",
892-
"plugin-review:unrated": "Qiymətləndirilməyib"
892+
"plugin-review:unrated": "Qiymətləndirilməyib",
893+
"faqs load failed": "Unable to load FAQs. Please try again.",
894+
"faqs empty": "No FAQs are available yet.",
895+
"retry": "Retry"
893896
}

0 commit comments

Comments
 (0)