M8Test Help

使用 Controller

页面导航与信息获取

本节演示如何加载页面并获取其标题和URL。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.1: 页面导航与信息获取 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.1: 页面导航与信息获取 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备测试用的HTML val html = """ <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> """ // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> controller.loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com") } .then { s, _ -> _console.log("页面加载指令已发出,等待渲染完成...") // 关键延时:等待页面内容渲染 s.delay(1000) } .then { s, _ -> controller.getTitle(s) } .then { s, title -> _console.log("页面标题:", title) _console.assertTrue(title == "M8Test 导航测试", "标题验证失败!") controller.getCurrentUrl(s) } .then { s, url -> _console.log("当前URL:", url) _console.assertTrue(url == "https://example.com/", "URL验证失败!") _console.info("--- 示例 2.1: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.1: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.1: 页面导航与信息获取 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.1: 页面导航与信息获取 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备测试用的HTML def html = """ <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> """ // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> return controller.loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com") } .then { s, _ -> $console.log("页面加载指令已发出,等待渲染完成...") // 关键延时:等待页面内容渲染 return s.delay(1000) } .then { s, _ -> return controller.getTitle(s) } .then { s, title -> $console.log("页面标题:", title) $console.assertTrue(title == "M8Test 导航测试", "标题验证失败!") return controller.getCurrentUrl(s) } .then { s, url -> $console.log("当前URL:", url) $console.assertTrue(url == "https://example.com/", "URL验证失败!") $console.info("--- 示例 2.1: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.1: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.1: 页面导航与信息获取 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.1: 页面导航与信息获取 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备测试用的HTML const html = ` <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> `; // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then((s, _) => { return controller.loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com"); }) .then((s, _) => { $console.log("页面加载指令已发出,等待渲染完成..."); // 关键延时:等待页面内容渲染 return s.delay(1000); }) .then((s, _) => { return controller.getTitle(s); }) .then((s, title) => { $console.log("页面标题:", title); $console.assertTrue(title == "M8Test 导航测试", "标题验证失败!"); return controller.getCurrentUrl(s); }) .then((s, url) => { $console.log("当前URL:", url); $console.assertTrue(url == "https://example.com/", "URL验证失败!"); $console.info("--- 示例 2.1: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.1: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.1: 页面导航与信息获取 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.1: 页面导航与信息获取 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备测试用的HTML local html = [[ <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> ]] -- 3. 开始异步操作链 scope:delay(1000) -- 关键延时:等待Activity和WebView初始化完成 :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com") end) :_then(function(s, _) _console:log("页面加载指令已发出,等待渲染完成...") -- 关键延时:等待页面内容渲染 return s:delay(1000) end) :_then(function(s, _) return controller:getTitle(s) end) :_then(function(s, title) _console:log("页面标题:", title) _console:assertTrue(title == "M8Test 导航测试", "标题验证失败!") return controller:getCurrentUrl(s) end) :_then(function(s, url) _console:log("当前URL:", url) _console:assertTrue(url == "https://example.com/", "URL验证失败!") _console:info("--- 示例 2.1: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.1: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.1: 页面导航与信息获取 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.1: 页面导航与信息获取 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备测试用的HTML $html = <<<HTML <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> HTML; // 3. 开始异步操作链 $scope->delay(1000) // 关键延时:等待Activity和WebView初始化完成 ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://example.com"), javaString($html), javaString("text/html"), javaString("UTF-8"), javaString("https://example.com")); }) ->then(function ($s, $unused) { global $console; $console->log(javaString("页面加载指令已发出,等待渲染完成...")); // 关键延时:等待页面内容渲染 return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller) { return $controller->getTitle($s); }) ->then(function ($s, $title) use ($controller) { global $console; $console->log(javaString("页面标题:"), $title); $console->assertTrue($title == javaString("M8Test 导航测试"), javaString("标题验证失败!")); return $controller->getCurrentUrl($s); }) ->then(function ($s, $url) { global $console; $console->log(javaString("当前URL:"), $url); $console->assertTrue($url == "https://example.com/", javaString("URL验证失败!")); $console->info(javaString("--- 示例 2.1: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.1: 失败 ---"), $e->stackTraceToString()); });
# ---------------------------------------------------- # 示例 2.1: 页面导航与信息获取 (完整代码) # ---------------------------------------------------- # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.1: 页面导航与信息获取 ---") # 1. 基础设置 # 【已修正】: 使用 getScriptMain() scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备测试用的HTML html = """ <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> """ # 3. 开始异步操作链 scope.delay(1000).then(lambda s, _: # 只有一个返回表达式,直接返回 controller.loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com") ).then(lambda s, _: ( _console.log("页面加载指令已发出,等待渲染完成..."), # 关键延时:等待页面内容渲染 s.delay(1000) )[-1]).then(lambda s, _: controller.getTitle(s) ).then(lambda s, title: ( _console.log("页面标题:", str(title)), _console.assertTrue(title == "M8Test 导航测试", "标题验证失败!"), controller.getCurrentUrl(s) )[-1]).then(lambda s, url: ( _console.log("当前URL:", str(url)), _console.assertTrue(url == "https://example.com/", "URL验证失败!"), _console.info("--- 示例 2.1: 成功 ---"), s.resolve(None) )[-1]).onError(lambda e: _console.error("--- 示例 2.1: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.1: 页面导航与信息获取 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.1: 页面导航与信息获取 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备测试用的HTML html = <<-HTML <html> <head><title>M8Test 导航测试</title></head> <body><h1>欢迎!</h1></body> </html> HTML # 3. 开始异步操作链 scope.delay(1000). # 关键延时:等待Activity和WebView初始化完成 then { |s, _| controller.loadDataWithBaseURL(s, "https://example.com", html, "text/html", "UTF-8", "https://example.com") }. then { |s, _| $console.log("页面加载指令已发出,等待渲染完成...") # 关键延时:等待页面内容渲染 s.delay(1000) }. then { |s, _| controller.getTitle(s) }. then { |s, title| $console.log("页面标题:", title) $console.assertTrue(title == "M8Test 导航测试", "标题验证失败!") controller.getCurrentUrl(s) }. then { |s, url| $console.log("当前URL:", url) $console.assertTrue(url == "https://example.com/", "URL验证失败!") $console.info("--- 示例 2.1: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.1: 失败 ---", e.to_s) }

查找并与页面元素交互

本节演示如何与输入框、下拉列表和按钮交互。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.2: 元素交互 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.2: 元素交互 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备HTML和XPath val html = """ <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> """ val inputXpath = "//*[@id='username']" val selectXpath = "//*[@id='role']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> controller.loadDataWithBaseURL(s, "https://form.test", html, null, null, "https://form.test") } .then { s, _ -> _console.log("页面加载指令已发出,等待渲染完成...") s.delay(1000) } .then { s, _ -> _console.log("清空输入框...") controller.clear(s, inputXpath) } .then { s, _ -> _console.log("输入新用户名 'M8Tester'...") controller.sendKeys(s, inputXpath, "M8Tester") } .then { s, _ -> _console.log("选择 '编辑' 角色...") controller.selectByVisibleText(s, selectXpath, "编辑") } .then { s, _ -> _console.log("点击提交按钮...") controller.click(s, "//*[@id='submitBtn']") } .then { s, _ -> _console.info("--- 示例 2.2: 成功 (所有交互已执行) ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.2: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.2: 元素交互 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.2: 元素交互 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备HTML和XPath def html = """ <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> """ def inputXpath = "//*[@id='username']" def selectXpath = "//*[@id='role']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> return controller.loadDataWithBaseURL(s, "https://form.test", html, null, null, "https://form.test") } .then { s, _ -> $console.log("页面加载指令已发出,等待渲染完成...") return s.delay(1000) } .then { s, _ -> $console.log("清空输入框...") return controller.clear(s, inputXpath) } .then { s, _ -> $console.log("输入新用户名 'M8Tester'...") return controller.sendKeys(s, inputXpath, "M8Tester") } .then { s, _ -> $console.log("选择 '编辑' 角色...") return controller.selectByVisibleText(s, selectXpath, "编辑") } .then { s, _ -> $console.log("点击提交按钮...") return controller.click(s, "//*[@id='submitBtn']") } .then { s, _ -> $console.info("--- 示例 2.2: 成功 (所有交互已执行) ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.2: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.2: 元素交互 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.2: 元素交互 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备HTML和XPath const html = ` <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> `; const inputXpath = "//*[@id='username']"; const selectXpath = "//*[@id='role']"; // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then((s, _) => { return controller.loadDataWithBaseURL(s, "https://form.test", html, null, null, "https://form.test"); }) .then((s, _) => { $console.log("页面加载指令已发出,等待渲染完成..."); return s.delay(1000); }) .then((s, _) => { $console.log("清空输入框..."); return controller.clear(s, inputXpath); }) .then((s, _) => { $console.log("输入新用户名 'M8Tester'..."); return controller.sendKeys(s, inputXpath, "M8Tester"); }) .then((s, _) => { $console.log("选择 '编辑' 角色..."); return controller.selectByVisibleText(s, selectXpath, "编辑"); }) .then((s, _) => { $console.log("点击提交按钮..."); return controller.click(s, "//*[@id='submitBtn']"); }) .then((s, _) => { $console.info("--- 示例 2.2: 成功 (所有交互已执行) ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.2: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.2: 元素交互 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.2: 元素交互 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备HTML和XPath local html = [[ <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> ]] local inputXpath = "//*[@id='username']" local selectXpath = "//*[@id='role']" -- 3. 开始异步操作链 scope:delay(1000) -- 关键延时:等待Activity和WebView初始化完成 :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://form.test", html, nil, nil, "https://form.test") end) :_then(function(s, _) _console:log("页面加载指令已发出,等待渲染完成...") return s:delay(1000) end) :_then(function(s, _) _console:log("清空输入框...") return controller:clear(s, inputXpath) end) :_then(function(s, _) _console:log("输入新用户名 'M8Tester'...") return controller:sendKeys(s, inputXpath, "M8Tester") end) :_then(function(s, _) _console:log("选择 '编辑' 角色...") return controller:selectByVisibleText(s, selectXpath, "编辑") end) :_then(function(s, _) _console:log("点击提交按钮...") return controller:click(s, "//*[@id='submitBtn']") end) :_then(function(s, _) _console:info("--- 示例 2.2: 成功 (所有交互已执行) ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.2: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.2: 元素交互 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.2: 元素交互 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备HTML和XPath $html = <<<HTML <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> HTML; $inputXpath = "//*[@id='username']"; $selectXpath = "//*[@id='role']"; // 3. 开始异步操作链 $scope->delay(1000) // 关键延时:等待Activity和WebView初始化完成 ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://form.test"), javaString($html), null, null, javaString("https://form.test")); }) ->then(function ($s, $unused) { global $console; $console->log(javaString("页面加载指令已发出,等待渲染完成...")); return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller, $inputXpath) { global $console; $console->log(javaString("清空输入框...")); return $controller->clear($s, $inputXpath); }) ->then(function ($s, $unused) use ($controller, $inputXpath) { global $console; $console->log(javaString("输入新用户名 'M8Tester'...")); return $controller->sendKeys($s, $inputXpath, javaString("M8Tester")); }) ->then(function ($s, $unused) use ($controller, $selectXpath) { global $console; $console->log(javaString("选择 '编辑' 角色...")); return $controller->selectByVisibleText($s, $selectXpath, javaString("编辑")); }) ->then(function ($s, $unused) use ($controller) { global $console; $console->log(javaString("点击提交按钮...")); return $controller->click($s, javaString("//*[@id='submitBtn']")); }) ->then(function ($s, $unused) { global $console; $console->info(javaString("--- 示例 2.2: 成功 (所有交互已执行) ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.2: 失败 ---"), $e->stackTraceToString()); });
# ---------------------------------------------------- # 示例 2.2: 元素交互 (完整代码) # ---------------------------------------------------- # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.2: 元素交互 ---") # 1. 基础设置 # 【已修正】: 使用 getScriptMain() scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备HTML和XPath html = """ <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> """ inputXpath = "//*[@id='username']" selectXpath = "//*[@id='role']" # 3. 开始异步操作链 scope.delay(1000).then(lambda s, _: controller.loadDataWithBaseURL(s, "https://form.test", html, None, None, "https://form.test") ).then(lambda s, _: ( _console.log("页面加载指令已发出,等待渲染完成..."), s.delay(1000) )[-1]).then(lambda s, _: ( _console.log("清空输入框..."), controller.clear(s, inputXpath) )[-1]).then(lambda s, _: ( _console.log("输入新用户名 'M8Tester'..."), controller.sendKeys(s, inputXpath, "M8Tester") )[-1]).then(lambda s, _: ( _console.log("选择 '编辑' 角色..."), controller.selectByVisibleText(s, selectXpath, "编辑") )[-1]).then(lambda s, _: ( _console.log("点击提交按钮..."), controller.click(s, "//*[@id='submitBtn']") )[-1]).then(lambda s, _: ( _console.info("--- 示例 2.2: 成功 (所有交互已执行) ---"), s.resolve(None) )[-1]).onError(lambda e: _console.error("--- 示例 2.2: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.2: 元素交互 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.2: 元素交互 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备HTML和XPath html = <<-HTML <html><body> <input type="text" id="username" value="admin"> <select id="role"> <option value="guest">访客</option> <option value="editor">编辑</option> </select> <button id="submitBtn">提交</button> </body></html> HTML input_xpath = "//*[@id='username']" select_xpath = "//*[@id='role']" # 3. 开始异步操作链 scope.delay(1000). # 关键延时:等待Activity和WebView初始化完成 then { |s, _| controller.loadDataWithBaseURL(s, "https://form.test", html, nil, nil, "https://form.test") }. then { |s, _| $console.log("页面加载指令已发出,等待渲染完成...") s.delay(1000) }. then { |s, _| $console.log("清空输入框...") controller.clear(s, input_xpath) }. then { |s, _| $console.log("输入新用户名 'M8Tester'...") controller.sendKeys(s, input_xpath, "M8Tester") }. then { |s, _| $console.log("选择 '编辑' 角色...") controller.selectByVisibleText(s, select_xpath, "编辑") }. then { |s, _| $console.log("点击提交按钮...") controller.click(s, "//*[@id='submitBtn']") }. then { |s, _| $console.info("--- 示例 2.2: 成功 (所有交互已执行) ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.2: 失败 ---", e.to_s) }

获取元素信息

本节演示如何从元素中提取文本、HTML属性和CSS样式。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.3: 获取元素信息 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.3: 获取元素信息 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备HTML和XPath val html = """ <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> """ val xpath = "//*[@id='link']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> controller.loadDataWithBaseURL(s, "https://info.test", html, null, null, "https://info.test") } .then { s, _ -> _console.log("页面加载指令已发出,等待渲染完成...") s.delay(1000) } .then { s, _ -> controller.getTextContent(s, xpath) } .then { s, text -> _console.log("文本内容 (getText):", text) controller.getAttribute(s, xpath, "href") } .then { s, href -> _console.log("href属性 (getAttribute):", href) controller.getAttribute(s, xpath, "data-id") } .then { s, dataId -> _console.log("data-id属性 (getAttribute):", dataId) controller.getCssValue(s, xpath, "color") } .then { s, color -> _console.log("CSS color (getCssValue):", color) _console.assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败") _console.info("--- 示例 2.3: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.3: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.3: 获取元素信息 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.3: 获取元素信息 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备HTML和XPath def html = """ <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> """ def xpath = "//*[@id='link']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> return controller.loadDataWithBaseURL(s, "https://info.test", html, null, null, "https://info.test") } .then { s, _ -> $console.log("页面加载指令已发出,等待渲染完成...") return s.delay(1000) } .then { s, _ -> controller.getTextContent(s, xpath) } .then { s, text -> $console.log("文本内容 (getText):", text) return controller.getAttribute(s, xpath, "href") } .then { s, href -> $console.log("href属性 (getAttribute):", href) return controller.getAttribute(s, xpath, "data-id") } .then { s, dataId -> $console.log("data-id属性 (getAttribute):", dataId) return controller.getCssValue(s, xpath, "color") } .then { s, color -> $console.log("CSS color (getCssValue):", color) $console.assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败") $console.info("--- 示例 2.3: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.3: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.3: 获取元素信息 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.3: 获取元素信息 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备HTML和XPath const html = ` <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> `; const xpath = "//*[@id='link']"; // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then((s, _) => { return controller.loadDataWithBaseURL(s, "https://info.test", html, null, null, "https://info.test"); }) .then((s, _) => { $console.log("页面加载指令已发出,等待渲染完成..."); return s.delay(1000); }) .then((s, _) => { return controller.getTextContent(s, xpath); }) .then((s, text) => { $console.log("文本内容 (getText):", text); return controller.getAttribute(s, xpath, "href"); }) .then((s, href) => { $console.log("href属性 (getAttribute):", href); return controller.getAttribute(s, xpath, "data-id"); }) .then((s, dataId) => { $console.log("data-id属性 (getAttribute):", dataId); return controller.getCssValue(s, xpath, "color"); }) .then((s, color) => { $console.log("CSS color (getCssValue):", color); $console.assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败"); $console.info("--- 示例 2.3: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.3: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.3: 获取元素信息 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.3: 获取元素信息 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备HTML和XPath local html = [[ <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> ]] local xpath = "//*[@id='link']" -- 3. 开始异步操作链 scope:delay(1000) -- 关键延时:等待Activity和WebView初始化完成 :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://info.test", html, nil, nil, "https://info.test") end) :_then(function(s, _) _console:log("页面加载指令已发出,等待渲染完成...") return s:delay(1000) end) :_then(function(s, _) return controller:getTextContent(s, xpath) end) :_then(function(s, text) _console:log("文本内容 (getText):", text) return controller:getAttribute(s, xpath, "href") end) :_then(function(s, href) _console:log("href属性 (getAttribute):", href) return controller:getAttribute(s, xpath, "data-id") end) :_then(function(s, dataId) _console:log("data-id属性 (getAttribute):", dataId) return controller:getCssValue(s, xpath, "color") end) :_then(function(s, color) _console:log("CSS color (getCssValue):", color) _console:assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败") _console:info("--- 示例 2.3: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.3: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.3: 获取元素信息 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.3: 获取元素信息 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备HTML和XPath $html = <<<HTML <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> HTML; $xpath = "//*[@id='link']"; // 3. 开始异步操作链 $scope->delay(1000) // 关键延时:等待Activity和WebView初始化完成 ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://info.test"), javaString($html), null, null, javaString("https://info.test")); }) ->then(function ($s, $unused) { global $console; $console->log(javaString("页面加载指令已发出,等待渲染完成...")); return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller, $xpath) { return $controller->getTextContent($s, $xpath); }) ->then(function ($s, $text) use ($controller, $xpath) { global $console; $console->log(javaString("文本内容 (getText):"), $text); return $controller->getAttribute($s, $xpath, javaString("href")); }) ->then(function ($s, $href) use ($controller, $xpath) { global $console; $console->log(javaString("href属性 (getAttribute):"), $href); return $controller->getAttribute($s, $xpath, javaString("data-id")); }) ->then(function ($s, $dataId) use ($controller, $xpath) { global $console; $console->log(javaString("data-id属性 (getAttribute):"), $dataId); return $controller->getCssValue($s, $xpath, javaString("color")); }) ->then(function ($s, $color) { global $console; $console->log(javaString("CSS color (getCssValue):"), $color); $console->assertTrue($color == "rgb(0, 0, 255)", javaString("CSS值验证失败")); $console->info(javaString("--- 示例 2.3: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.3: 失败 ---"), $e->stackTraceToString()); });
# ---------------------------------------------------- # 示例 2.3: 获取元素信息 (完整代码) # ---------------------------------------------------- # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.3: 获取元素信息 ---") # 1. 基础设置 # 【已修正】: 使用 getScriptMain() scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备HTML和XPath html = """ <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> """ xpath = "//*[@id='link']" # 3. 开始异步操作链 scope.delay(1000).then(lambda s, _: controller.loadDataWithBaseURL(s, "https://info.test", html, None, None, "https://info.test") ).then(lambda s, _: ( _console.log("页面加载指令已发出,等待渲染完成..."), s.delay(1000) )[-1]).then(lambda s, _: controller.getTextContent(s, xpath) ).then(lambda s, text: ( _console.log("文本内容 (getText):", str(text)), controller.getAttribute(s, xpath, "href") )[-1]).then(lambda s, href: ( _console.log("href属性 (getAttribute):", str(href)), controller.getAttribute(s, xpath, "data-id") )[-1]).then(lambda s, dataId: ( _console.log("data-id属性 (getAttribute):", str(dataId)), controller.getCssValue(s, xpath, "color") )[-1]).then(lambda s, color: ( _console.log("CSS color (getCssValue):", str(color)), _console.assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败"), _console.info("--- 示例 2.3: 成功 ---"), s.resolve(None) )[-1]).onError(lambda e: _console.error("--- 示例 2.3: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.3: 获取元素信息 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.3: 获取元素信息 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备HTML和XPath html = <<-HTML <html> <head><style> a { color: rgb(0, 0, 255); } </style></head> <body> <a id="link" href="https://m8test.com" data-id="123"><b>M8Test官网</b></a> </body> </html> HTML xpath = "//*[@id='link']" # 3. 开始异步操作链 scope.delay(1000). # 关键延时:等待Activity和WebView初始化完成 then { |s, _| controller.loadDataWithBaseURL(s, "https://info.test", html, nil, nil, "https://info.test") }. then { |s, _| $console.log("页面加载指令已发出,等待渲染完成...") s.delay(1000) }. then { |s, _| controller.getTextContent(s, xpath) }. then { |s, text| $console.log("文本内容 (getText):", text) controller.getAttribute(s, xpath, "href") }. then { |s, href| $console.log("href属性 (getAttribute):", href) controller.getAttribute(s, xpath, "data-id") }. then { |s, data_id| $console.log("data-id属性 (getAttribute):", data_id) controller.getCssValue(s, xpath, "color") }. then { |s, color| $console.log("CSS color (getCssValue):", color) $console.assertTrue(color == "rgb(0, 0, 255)", "CSS值验证失败") $console.info("--- 示例 2.3: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.3: 失败 ---", e.to_s) }

状态检查与等待

本节演示如何正确处理延迟加载的元素。waitForElementVisible 是比固定延时更可靠的等待方式。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.4: 状态检查与等待 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.4: 状态检查与等待 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备动态HTML val html = """ <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> """ val xpath = "//*[@id='content']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> controller.loadDataWithBaseURL(s, "https://wait.test", html, null, null, "https://wait.test") } .then { s, _ -> _console.log("页面已加载,开始使用智能等待动态元素...") // 使用 waitForVisible 代替固定延时,更可靠 controller.waitForVisible(s, xpath, 5000) } .then { s, becameVisible -> _console.log("元素是否在超时前变得可见:", becameVisible) _console.assertTrue(becameVisible, "等待元素可见超时!") controller.getTextContent(s, xpath) } .then { s, text -> _console.log("获取到动态内容:", text) _console.assertTrue(text == "最终内容", "动态内容验证失败") _console.info("--- 示例 2.4: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.4: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.4: 状态检查与等待 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.4: 状态检查与等待 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备动态HTML def html = """ <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> """ def xpath = "//*[@id='content']" // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then { s, _ -> return controller.loadDataWithBaseURL(s, "https://wait.test", html, null, null, "https://wait.test") } .then { s, _ -> $console.log("页面已加载,开始使用智能等待动态元素...") // 使用 waitForVisible 代替固定延时,更可靠 return controller.waitForVisible(s, xpath, 5000) } .then { s, becameVisible -> $console.log("元素是否在超时前变得可见:", becameVisible) $console.assertTrue(becameVisible, "等待元素可见超时!") return controller.getTextContent(s, xpath) } .then { s, text -> $console.log("获取到动态内容:", text) $console.assertTrue(text == "最终内容", "动态内容验证失败") $console.info("--- 示例 2.4: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.4: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.4: 状态检查与等待 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.4: 状态检查与等待 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备动态HTML const html = ` <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> `; const xpath = "//*[@id='content']"; // 3. 开始异步操作链 scope.delay(1000) // 关键延时:等待Activity和WebView初始化完成 .then((s, _) => { return controller.loadDataWithBaseURL(s, "https://wait.test", html, null, null, "https://wait.test"); }) .then((s, _) => { $console.log("页面已加载,开始使用智能等待动态元素..."); // 使用 waitForVisible 代替固定延时,更可靠 return controller.waitForVisible(s, xpath, 5000); }) .then((s, becameVisible) => { $console.log("元素是否在超时前变得可见:", becameVisible); $console.assertTrue(becameVisible, "等待元素可见超时!"); return controller.getTextContent(s, xpath); }) .then((s, text) => { $console.log("获取到动态内容:", text); $console.assertTrue(text == "最终内容", "动态内容验证失败"); $console.info("--- 示例 2.4: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.4: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.4: 状态检查与等待 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.4: 状态检查与等待 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备动态HTML local html = [[ <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> ]] local xpath = "//*[@id='content']" -- 3. 开始异步操作链 scope:delay(1000) -- 关键延时:等待Activity和WebView初始化完成 :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://wait.test", html, nil, nil, "https://wait.test") end) :_then(function(s, _) _console:log("页面已加载,开始使用智能等待动态元素...") -- 使用 waitForVisible 代替固定延时,更可靠 return controller:waitForVisible(s, xpath, 5000) end) :_then(function(s, becameVisible) _console:log("元素是否在超时前变得可见:", becameVisible) _console:assertTrue(becameVisible, "等待元素可见超时!") return controller:getTextContent(s, xpath) end) :_then(function(s, text) _console:log("获取到动态内容:", text) _console:assertTrue(text == "最终内容", "动态内容验证失败") _console:info("--- 示例 2.4: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.4: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.4: 状态检查与等待 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.4: 状态检查与等待 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备动态HTML $html = <<<HTML <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> HTML; $xpath = "//*[@id='content']"; // 3. 开始异步操作链 $scope->delay(1000) // 关键延时:等待Activity和WebView初始化完成 ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://wait.test"), javaString($html), null, null, javaString("https://wait.test")); }) ->then(function ($s, $unused) use ($controller, $xpath) { global $console; $console->log(javaString("页面已加载,开始使用智能等待动态元素...")); // 使用 waitForVisible 代替固定延时,更可靠 return $controller->waitForVisible($s, $xpath, 5000); }) ->then(function ($s, $becameVisible) use ($controller, $xpath) { global $console; $console->log(javaString("元素是否在超时前变得可见:"), $becameVisible); $console->assertTrue($becameVisible, javaString("等待元素可见超时!")); return $controller->getTextContent($s, $xpath); }) ->then(function ($s, $text) { global $console; $console->log(javaString("获取到动态内容:"), $text); $console->assertTrue($text == javaString("最终内容"), javaString("动态内容验证失败")); $console->info(javaString("--- 示例 2.4: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.4: 失败 ---"), $e->stackTraceToString()); });
# ---------------------------------------------------- # 示例 2.4: 状态检查与等待 (完整代码) # ---------------------------------------------------- # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.4: 状态检查与等待 ---") # 1. 基础设置 # 【已修正】: 使用 getScriptMain() scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备动态HTML html = """ <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> """ xpath = "//*[@id='content']" # 3. 开始异步操作链 scope.delay(1000).then(lambda s, _: controller.loadDataWithBaseURL(s, "https://wait.test", html, None, None, "https://wait.test") ).then(lambda s, _: ( _console.log("页面已加载,开始使用智能等待动态元素..."), # 使用 waitForVisible 代替固定延时,更可靠 controller.waitForVisible(s, xpath, 5000) )[-1]).then(lambda s, becameVisible: ( _console.log("元素是否在超时前变得可见:", str(becameVisible)), _console.assertTrue(becameVisible, "等待元素可见超时!"), controller.getTextContent(s, xpath) )[-1]).then(lambda s, text: ( _console.log("获取到动态内容:", str(text)), _console.assertTrue(text == "最终内容", "动态内容验证失败"), _console.info("--- 示例 2.4: 成功 ---"), s.resolve(None) )[-1]).onError(lambda e: _console.error("--- 示例 2.4: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.4: 状态检查与等待 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.4: 状态检查与等待 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备动态HTML html = <<-HTML <html><body> <div id="content" style="display:none;">最终内容</div> <script> setTimeout(() => { document.getElementById('content').style.display = 'block'; }, 2000); </script> </body></html> HTML xpath = "//*[@id='content']" # 3. 开始异步操作链 scope.delay(1000). # 关键延时:等待Activity和WebView初始化完成 then { |s, _| controller.loadDataWithBaseURL(s, "https://wait.test", html, nil, nil, "https://wait.test") }. then { |s, _| $console.log("页面已加载,开始使用智能等待动态元素...") # 使用 waitForVisible 代替固定延时,更可靠 controller.waitForVisible(s, xpath, 5000) }. then { |s, became_visible| $console.log("元素是否在超时前变得可见:", became_visible) $console.assertTrue(became_visible, "等待元素可见超时!") controller.getTextContent(s, xpath) }. then { |s, text| $console.log("获取到动态内容:", text) $console.assertTrue(text == "最终内容", "动态内容验证失败") $console.info("--- 示例 2.4: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.4: 失败 ---", e.to_s) }

页面历史与重载

本节演示如何通过代码控制WebView的前进、后退操作,以及如何刷新当前页面。

  • reload(): 重新加载当前页面。

  • back(): 导航到历史记录中的上一页。

  • forward(): 导航到历史记录中的下一页。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.5: 页面历史与重载 (修正版 - 完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.5: 页面历史与重载 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备两个不同的HTML页面内容 val htmlPage1 = """ <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> """ val htmlPage2 = """ <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> """ // 3. 开始异步操作链 scope.delay(1000) // 等待Activity初始化 .then { s, _ -> _console.log("加载第一页...") controller.loadDataWithBaseURL(s, "https://history.test/page1", htmlPage1, null, null, "https://history.test/page1") } .then { s, _ -> s.delay(1000) } // 等待第一页渲染 .then { s, _ -> _console.log("加载第二页,创建历史记录...") // 模拟导航到第二个页面 controller.loadDataWithBaseURL(s, "https://history.test/page2", htmlPage2, null, null, "https://history.test/page2") } .then { s, _ -> s.delay(1000) } // 等待第二页渲染 .then { s, _ -> controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> _console.log("当前页面内容:", text) _console.assertTrue(text == "这是第二页", "未能成功导航到第二页") _console.log("执行 'back()' 后退操作...") controller.back(s) } .then { s, _ -> s.delay(1000) } // 等待后退导航完成 .then { s, _ -> controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> _console.log("后退后的页面内容:", text) _console.assertTrue(text == "这是第一页", "后退操作失败") _console.log("执行 'forward()' 前进操作...") controller.forward(s) } .then { s, _ -> s.delay(1000) } // 等待前进导航完成 .then { s, _ -> controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> _console.log("前进后的页面内容:", text) _console.assertTrue(text == "这是第二页", "前进操作失败") _console.info("--- 示例 2.5: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.5: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.5: 页面历史与重载 (修正版 - 完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.5: 页面历史与重载 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备两个不同的HTML页面内容 def htmlPage1 = """ <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> """ def htmlPage2 = """ <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> """ // 3. 开始异步操作链 scope.delay(1000) // 等待Activity初始化 .then { s, _ -> $console.log("加载第一页...") return controller.loadDataWithBaseURL(s, "https://history.test/page1", htmlPage1, null, null, "https://history.test/page1") } .then { s, _ -> s.delay(1000) } // 等待第一页渲染 .then { s, _ -> $console.log("加载第二页,创建历史记录...") // 模拟导航到第二个页面 return controller.loadDataWithBaseURL(s, "https://history.test/page2", htmlPage2, null, null, "https://history.test/page2") } .then { s, _ -> s.delay(1000) } // 等待第二页渲染 .then { s, _ -> return controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> $console.log("当前页面内容:", text) $console.assertTrue(text == "这是第二页", "未能成功导航到第二页") $console.log("执行 'back()' 后退操作...") return controller.back(s) } .then { s, _ -> s.delay(1000) } // 等待后退导航完成 .then { s, _ -> return controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> $console.log("后退后的页面内容:", text) $console.assertTrue(text == "这是第一页", "后退操作失败") $console.log("执行 'forward()' 前进操作...") return controller.forward(s) } .then { s, _ -> s.delay(1000) } // 等待前进导航完成 .then { s, _ -> return controller.getTextContent(s, "//*[@id='page-title']") } .then { s, text -> $console.log("前进后的页面内容:", text) $console.assertTrue(text == "这是第二页", "前进操作失败") $console.info("--- 示例 2.5: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.5: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.5: 页面历史与重载 (修正版 - 完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.5: 页面历史与重载 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备两个不同的HTML页面内容 const htmlPage1 = ` <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> `; const htmlPage2 = ` <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> `; // 3. 开始异步操作链 scope.delay(1000) // 等待Activity初始化 .then((s, _) => { $console.log("加载第一页..."); return controller.loadDataWithBaseURL(s, "https://history.test/page1", htmlPage1, null, null, "https://history.test/page1"); }) .then((s, _) => s.delay(1000)) // 等待第一页渲染 .then((s, _) => { $console.log("加载第二页,创建历史记录..."); // 模拟导航到第二个页面 return controller.loadDataWithBaseURL(s, "https://history.test/page2", htmlPage2, null, null, "https://history.test/page2"); }) .then((s, _) => s.delay(1000)) // 等待第二页渲染 .then((s, _) => { return controller.getTextContent(s, "//*[@id='page-title']"); }) .then((s, text) => { $console.log("当前页面内容:", text); $console.assertTrue(text == "这是第二页", "未能成功导航到第二页"); $console.log("执行 'back()' 后退操作..."); return controller.back(s); }) .then((s, _) => s.delay(1000)) // 等待后退导航完成 .then((s, _) => { return controller.getTextContent(s, "//*[@id='page-title']"); }) .then((s, text) => { $console.log("后退后的页面内容:", text); $console.assertTrue(text == "这是第一页", "后退操作失败"); $console.log("执行 'forward()' 前进操作..."); return controller.forward(s); }) .then((s, _) => s.delay(1000)) // 等待前进导航完成 .then((s, _) => { return controller.getTextContent(s, "//*[@id='page-title']"); }) .then((s, text) => { $console.log("前进后的页面内容:", text); $console.assertTrue(text == "这是第二页", "前进操作失败"); $console.info("--- 示例 2.5: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.5: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.5: 页面历史与重载 (修正版 - 完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.5: 页面历史与重载 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备两个不同的HTML页面内容 local htmlPage1 = [[ <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> ]] local htmlPage2 = [[ <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> ]] -- 3. 开始异步操作链 scope:delay(1000) -- 等待Activity初始化 :_then(function(s, _) _console:log("加载第一页...") return controller:loadDataWithBaseURL(s, "https://history.test/page1", htmlPage1, nil, nil, "https://history.test/page1") end) :_then(function(s, _) return s:delay(1000) end) -- 等待第一页渲染 :_then(function(s, _) _console:log("加载第二页,创建历史记录...") -- 模拟导航到第二个页面 return controller:loadDataWithBaseURL(s, "https://history.test/page2", htmlPage2, nil, nil, "https://history.test/page2") end) :_then(function(s, _) return s:delay(1000) end) -- 等待第二页渲染 :_then(function(s, _) return controller:getTextContent(s, "//*[@id='page-title']") end) :_then(function(s, text) _console:log("当前页面内容:", text) _console:assertTrue(text == "这是第二页", "未能成功导航到第二页") _console:log("执行 'back()' 后退操作...") return controller:back(s) end) :_then(function(s, _) return s:delay(1000) end) -- 等待后退导航完成 :_then(function(s, _) return controller:getTextContent(s, "//*[@id='page-title']") end) :_then(function(s, text) _console:log("后退后的页面内容:", text) _console:assertTrue(text == "这是第一页", "后退操作失败") _console:log("执行 'forward()' 前进操作...") return controller:forward(s) end) :_then(function(s, _) return s:delay(1000) end) -- 等待前进导航完成 :_then(function(s, _) return controller:getTextContent(s, "//*[@id='page-title']") end) :_then(function(s, text) _console:log("前进后的页面内容:", text) _console:assertTrue(text == "这是第二页", "前进操作失败") _console:info("--- 示例 2.5: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.5: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.5: 页面历史与重载 (修正版 - 完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.5: 页面历史与重载 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备两个不同的HTML页面内容 $htmlPage1 = <<<HTML <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> HTML; $htmlPage2 = <<<HTML <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> HTML; // 3. 开始异步操作链 $scope->delay(1000) // 等待Activity初始化 ->then(function ($s, $unused) use ($controller, $htmlPage1) { global $console; $console->log(javaString("加载第一页...")); return $controller->loadDataWithBaseURL($s, javaString("https://history.test/page1"), javaString($htmlPage1), null, null, javaString("https://history.test/page1")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待第一页渲染 ->then(function ($s, $unused) use ($controller, $htmlPage2) { global $console; $console->log(javaString("加载第二页,创建历史记录...")); // 模拟导航到第二个页面 return $controller->loadDataWithBaseURL($s, javaString("https://history.test/page2"), javaString($htmlPage2), null, null, javaString("https://history.test/page2")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待第二页渲染 ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='page-title']")); }) ->then(function ($s, $text) use ($controller) { global $console; $console->log(javaString("当前页面内容:"), $text); $console->assertTrue($text == javaString("这是第二页"), javaString("未能成功导航到第二页")); $console->log(javaString("执行 'back()' 后退操作...")); return $controller->back($s); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待后退导航完成 ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='page-title']")); }) ->then(function ($s, $text) use ($controller) { global $console; $console->log(javaString("后退后的页面内容:"), $text); $console->assertTrue($text == javaString("这是第一页"), javaString("后退操作失败")); $console->log(javaString("执行 'forward()' 前进操作...")); return $controller->forward($s); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待前进导航完成 ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='page-title']")); }) ->then(function ($s, $text) { global $console; $console->log(javaString("前进后的页面内容:"), $text); $console->assertTrue($text == javaString("这是第二页"), javaString("前进操作失败")); $console->info(javaString("--- 示例 2.5: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.5: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.5: 页面历史与重载 (修正版 - 完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.5: 页面历史与重载 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备两个不同的HTML页面内容 htmlPage1 = """ <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> """ htmlPage2 = """ <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> """ # 3. 开始异步操作链 scope.delay(1000).then(lambda s, _: ( _console.log("加载第一页..."), controller.loadDataWithBaseURL(s, "https://history.test/page1", htmlPage1, None, None, "https://history.test/page1") )[-1]).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("加载第二页,创建历史记录..."), controller.loadDataWithBaseURL(s, "https://history.test/page2", htmlPage2, None, None, "https://history.test/page2") )[-1]).then( lambda s, _: s.delay(1000) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='page-title']") ).then(lambda s, text: ( _console.log("当前页面内容:", text), _console.assertTrue(text == "这是第二页", "未能成功导航到第二页"), _console.log("执行 'back()' 后退操作..."), controller.back(s) )[-1]).then( lambda s, _: s.delay(1000) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='page-title']") ).then(lambda s, text: ( _console.log("后退后的页面内容:", text), _console.assertTrue(text == "这是第一页", "后退操作失败"), _console.log("执行 'forward()' 前进操作..."), controller.forward(s) )[-1]).then( lambda s, _: s.delay(1000) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='page-title']") ).then(lambda s, text: ( _console.log("前进后的页面内容:", text), _console.assertTrue(text == "这是第二页", "前进操作失败"), _console.info("--- 示例 2.5: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.5: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.5: 页面历史与重载 (修正版 - 完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.5: 页面历史与重载 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备两个不同的HTML页面内容 html_page1 = <<-HTML <html> <head><title>第一页</title></head> <body><h1 id="page-title">这是第一页</h1></body> </html> HTML html_page2 = <<-HTML <html> <head><title>第二页</title></head> <body><h1 id="page-title">这是第二页</h1></body> </html> HTML # 3. 开始异步操作链 scope.delay(1000). # 等待Activity初始化 then { |s, _| $console.log("加载第一页...") controller.loadDataWithBaseURL(s, "https://history.test/page1", html_page1, nil, nil, "https://history.test/page1") }. then { |s, _| s.delay(1000) }. # 等待第一页渲染 then { |s, _| $console.log("加载第二页,创建历史记录...") # 模拟导航到第二个页面 controller.loadDataWithBaseURL(s, "https://history.test/page2", html_page2, nil, nil, "https://history.test/page2") }. then { |s, _| s.delay(1000) }. # 等待第二页渲染 then { |s, _| controller.getTextContent(s, "//*[@id='page-title']") }. then { |s, text| $console.log("当前页面内容:", text) $console.assertTrue(text == "这是第二页", "未能成功导航到第二页") $console.log("执行 'back()' 后退操作...") controller.back(s) }. then { |s, _| s.delay(1000) }. # 等待后退导航完成 then { |s, _| controller.getTextContent(s, "//*[@id='page-title']") }. then { |s, text| $console.log("后退后的页面内容:", text) $console.assertTrue(text == "这是第一页", "后退操作失败") $console.log("执行 'forward()' 前进操作...") controller.forward(s) }. then { |s, _| s.delay(1000) }. # 等待前进导航完成 then { |s, _| controller.getTextContent(s, "//*[@id='page-title']") }. then { |s, text| $console.log("前进后的页面内容:", text) $console.assertTrue(text == "这是第二页", "前进操作失败") $console.info("--- 示例 2.5: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.5: 失败 ---", e.to_s) }

高级元素交互

本节涵盖了更复杂的用户交互模拟,如双击、右键点击和拖放操作。

  • doubleClick(xpath): 模拟双击一个元素。

  • contextClick(xpath): 模拟右键点击一个元素。

  • dragAndDrop(sourceXpath, targetXpath): 将一个元素拖拽到另一个元素上。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.6: 高级元素交互 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.6: 高级元素交互 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备可以响应高级交互事件的HTML val html = """ <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://advanced.test", html, null, null, "https://advanced.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> _console.log("测试双击...") controller.doubleClick(s, "//*[@id='dblclick-box']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='dblclick-box']") } .then { s, text -> _console.assertTrue(text == "双击成功!", "双击测试失败") _console.log("测试右键点击...") controller.contextClick(s, "//*[@id='context-box']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='context-box']") } .then { s, text -> _console.assertTrue(text == "右键成功!", "右键点击测试失败") _console.log("测试拖放...") controller.dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='drop-target']") } .then { s, text -> _console.assertTrue(text == "拖放成功!", "拖放测试失败") _console.info("--- 示例 2.6: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.6: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.6: 高级元素交互 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.6: 高级元素交互 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备可以响应高级交互事件的HTML def html = """ <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://advanced.test", html, null, null, "https://advanced.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> $console.log("测试双击...") return controller.doubleClick(s, "//*[@id='dblclick-box']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='dblclick-box']") } .then { s, text -> $console.assertTrue(text == "双击成功!", "双击测试失败") $console.log("测试右键点击...") return controller.contextClick(s, "//*[@id='context-box']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='context-box']") } .then { s, text -> $console.assertTrue(text == "右键成功!", "右键点击测试失败") $console.log("测试拖放...") return controller.dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getTextContent(s, "//*[@id='drop-target']") } .then { s, text -> $console.assertTrue(text == "拖放成功!", "拖放测试失败") $console.info("--- 示例 2.6: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.6: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.6: 高级元素交互 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.6: 高级元素交互 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备可以响应高级交互事件的HTML const html = ` <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> `; // 3. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, "https://advanced.test", html, null, null, "https://advanced.test")) .then((s, _) => s.delay(1000)) .then((s, _) => { $console.log("测试双击..."); return controller.doubleClick(s, "//*[@id='dblclick-box']"); }) .then((s, _) => s.delay(500)) .then((s, _) => controller.getTextContent(s, "//*[@id='dblclick-box']")) .then((s, text) => { $console.assertTrue(text == "双击成功!", "双击测试失败"); $console.log("测试右键点击..."); return controller.contextClick(s, "//*[@id='context-box']"); }) .then((s, _) => s.delay(500)) .then((s, _) => controller.getTextContent(s, "//*[@id='context-box']")) .then((s, text) => { $console.assertTrue(text == "右键成功!", "右键点击测试失败"); $console.log("测试拖放..."); return controller.dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']"); }) .then((s, _) => s.delay(500)) .then((s, _) => controller.getTextContent(s, "//*[@id='drop-target']")) .then((s, text) => { $console.assertTrue(text == "拖放成功!", "拖放测试失败"); $console.info("--- 示例 2.6: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.6: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.6: 高级元素交互 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.6: 高级元素交互 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备可以响应高级交互事件的HTML local html = [[ <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> ]] -- 3. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://advanced.test", html, nil, nil, "https://advanced.test") end) :_then(function(s, _) return s:delay(1000) end) :_then(function(s, _) _console:log("测试双击...") return controller:doubleClick(s, "//*[@id='dblclick-box']") end) :_then(function(s, _) return s:delay(500) end) :_then(function(s, _) return controller:getTextContent(s, "//*[@id='dblclick-box']") end) :_then(function(s, text) _console:assertTrue(text == "双击成功!", "双击测试失败") _console:log("测试右键点击...") return controller:contextClick(s, "//*[@id='context-box']") end) :_then(function(s, _) return s:delay(500) end) :_then(function(s, _) return controller:getTextContent(s, "//*[@id='context-box']") end) :_then(function(s, text) _console:assertTrue(text == "右键成功!", "右键点击测试失败") _console:log("测试拖放...") return controller:dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']") end) :_then(function(s, _) return s:delay(500) end) :_then(function(s, _) return controller:getTextContent(s, "//*[@id='drop-target']") end) :_then(function(s, text) _console:assertTrue(text == "拖放成功!", "拖放测试失败") _console:info("--- 示例 2.6: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.6: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.6: 高级元素交互 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.6: 高级元素交互 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备可以响应高级交互事件的HTML $html = <<<HTML <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> HTML; // 3. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://advanced.test"), javaString($html), null, null, javaString("https://advanced.test")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller) { global $console; $console->log(javaString("测试双击...")); return $controller->doubleClick($s, javaString("//*[@id='dblclick-box']")); }) ->then(function ($s, $unused) { return $s->delay(500); }) ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='dblclick-box']")); }) ->then(function ($s, $text) use ($controller) { global $console; $console->assertTrue($text == javaString("双击成功!"), javaString("双击测试失败")); $console->log(javaString("测试右键点击...")); return $controller->contextClick($s, javaString("//*[@id='context-box']")); }) ->then(function ($s, $unused) { return $s->delay(500); }) ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='context-box']")); }) ->then(function ($s, $text) use ($controller) { global $console; $console->assertTrue($text == javaString("右键成功!"), javaString("右键点击测试失败")); $console->log(javaString("测试拖放...")); return $controller->dragAndDrop($s, javaString("//*[@id='draggable']"), javaString("//*[@id='drop-target']")); }) ->then(function ($s, $unused) { return $s->delay(500); }) ->then(function ($s, $unused) use ($controller) { return $controller->getTextContent($s, javaString("//*[@id='drop-target']")); }) ->then(function ($s, $text) { global $console; $console->assertTrue($text == javaString("拖放成功!"), javaString("拖放测试失败")); $console->info(javaString("--- 示例 2.6: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.6: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.6: 高级元素交互 (完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.6: 高级元素交互 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备可以响应高级交互事件的HTML html = """ <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> """ # 3. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, "https://advanced.test", html, None, None, "https://advanced.test") ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("测试双击..."), controller.doubleClick(s, "//*[@id='dblclick-box']") )[-1]).then( lambda s, _: s.delay(500) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='dblclick-box']") ).then(lambda s, text: ( _console.assertTrue(text == "双击成功!", "双击测试失败"), _console.log("测试右键点击..."), controller.contextClick(s, "//*[@id='context-box']") )[-1]).then( lambda s, _: s.delay(500) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='context-box']") ).then(lambda s, text: ( _console.assertTrue(text == "右键成功!", "右键点击测试失败"), _console.log("测试拖放..."), controller.dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']") )[-1]).then( lambda s, _: s.delay(500) ).then( lambda s, _: controller.getTextContent(s, "//*[@id='drop-target']") ).then(lambda s, text: ( _console.assertTrue(text == "拖放成功!", "拖放测试失败"), _console.info("--- 示例 2.6: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.6: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.6: 高级元素交互 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.6: 高级元素交互 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备可以响应高级交互事件的HTML html = <<-HTML <html> <head> <style> div { border: 1px solid #ccc; padding: 10px; margin: 10px; user-select: none; } #drop-target { background-color: #f0f0f0; } </style> </head> <body> <div id="dblclick-box" ondblclick="this.innerText='双击成功!'">请双击这里</div> <div id="context-box" oncontextmenu="this.innerText='右键成功!'; return false;">请右键点击这里</div> <div id="draggable" draggable="true">拖拽我</div> <div id="drop-target" ondrop="this.innerText='拖放成功!'" ondragover="event.preventDefault();">到这里</div> </body> </html> HTML # 3. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, "https://advanced.test", html, nil, nil, "https://advanced.test") }. then { |s, _| s.delay(1000) }. then { |s, _| $console.log("测试双击...") controller.doubleClick(s, "//*[@id='dblclick-box']") }. then { |s, _| s.delay(500) }. then { |s, _| controller.getTextContent(s, "//*[@id='dblclick-box']") }. then { |s, text| $console.assertTrue(text == "双击成功!", "双击测试失败") $console.log("测试右键点击...") controller.contextClick(s, "//*[@id='context-box']") }. then { |s, _| s.delay(500) }. then { |s, _| controller.getTextContent(s, "//*[@id='context-box']") }. then { |s, text| $console.assertTrue(text == "右键成功!", "右键点击测试失败") $console.log("测试拖放...") controller.dragAndDrop(s, "//*[@id='draggable']", "//*[@id='drop-target']") }. then { |s, _| s.delay(500) }. then { |s, _| controller.getTextContent(s, "//*[@id='drop-target']") }. then { |s, text| $console.assertTrue(text == "拖放成功!", "拖放测试失败") $console.info("--- 示例 2.6: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.6: 失败 ---", e.to_s) }

键盘与滚动操作

本节展示如何模拟键盘按键,以及如何控制页面的滚动。

  • pressKey(xpath, keySequence): 在指定元素上模拟按键,支持组合键如 "Ctrl+C"。

  • scrollIntoView(xpath): 将指定元素滚动到浏览器可视区域内。

  • scrollTo(x, y) / scrollBy(dx, dy): 将页面滚动到绝对位置或按相对距离滚动。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.7: 键盘与滚动操作 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.7: 键盘与滚动操作 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备一个长页面用于滚动 val html = """ <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://scroll.test", html, null, null, "https://scroll.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> _console.log("测试按键 'Enter'...") controller.pressKey(s, "//*[@id='key-input']", "Enter") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getProperty(s, "//*[@id='key-input']", "value") } .then { s, text -> _console.assertTrue(text == "Enter pressed!", "按键测试失败") _console.log("测试滚动到页面底部元素...") controller.scrollIntoView(s, "//*[@id='bottom-element']") } .then { s, _ -> s.delay(1000) } // 等待滚动动画 .then { s, _ -> // 验证滚动的一种方法是检查元素现在是否可见 controller.isDisplayed(s, "//*[@id='bottom-element']") } .then { s, isDisplayed -> _console.assertTrue(isDisplayed, "滚动测试失败,元素未变得可见") _console.info("--- 示例 2.7: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.7: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.7: 键盘与滚动操作 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.7: 键盘与滚动操作 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备一个长页面用于滚动 def html = """ <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://scroll.test", html, null, null, "https://scroll.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> $console.log("测试按键 'Enter'...") return controller.pressKey(s, "//*[@id='key-input']", "Enter") } .then { s, _ -> s.delay(500) } .then { s, _ -> controller.getProperty(s, "//*[@id='key-input']", "value") } .then { s, text -> $console.assertTrue(text == "Enter pressed!", "按键测试失败") $console.log("测试滚动到页面底部元素...") return controller.scrollIntoView(s, "//*[@id='bottom-element']") } .then { s, _ -> s.delay(1000) } // 等待滚动动画 .then { s, _ -> // 验证滚动的一种方法是检查元素现在是否可见 return controller.isDisplayed(s, "//*[@id='bottom-element']") } .then { s, isDisplayed -> $console.assertTrue(isDisplayed, "滚动测试失败,元素未变得可见") $console.info("--- 示例 2.7: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.7: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.7: 键盘与滚动操作 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.7: 键盘与滚动操作 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备一个长页面用于滚动 const html = ` <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> `; // 3. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, "https://scroll.test", html, null, null, "https://scroll.test")) .then((s, _) => s.delay(1000)) .then((s, _) => { $console.log("测试按键 'Enter'..."); return controller.pressKey(s, "//*[@id='key-input']", "Enter"); }) .then((s, _) => s.delay(500)) .then((s, _) => controller.getProperty(s, "//*[@id='key-input']", "value")) .then((s, text) => { $console.assertTrue(text == "Enter pressed!", "按键测试失败"); $console.log("测试滚动到页面底部元素..."); return controller.scrollIntoView(s, "//*[@id='bottom-element']"); }) .then((s, _) => s.delay(1000)) // 等待滚动动画 .then((s, _) => { // 验证滚动的一种方法是检查元素现在是否可见 return controller.isDisplayed(s, "//*[@id='bottom-element']"); }) .then((s, isDisplayed) => { $console.assertTrue(isDisplayed == true, "滚动测试失败,元素未变得可见"); $console.info("--- 示例 2.7: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.7: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.7: 键盘与滚动操作 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.7: 键盘与滚动操作 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备一个长页面用于滚动 local html = [[ <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> ]] -- 3. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://scroll.test", html, nil, nil, "https://scroll.test") end) :_then(function(s, _) return s:delay(1000) end) :_then(function(s, _) _console:log("测试按键 'Enter'...") return controller:pressKey(s, "//*[@id='key-input']", "Enter") end) :_then(function(s, _) return s:delay(500) end) :_then(function(s, _) return controller:getProperty(s, "//*[@id='key-input']", "value") end) :_then(function(s, text) _console:assertTrue(text == "Enter pressed!", "按键测试失败") _console:log("测试滚动到页面底部元素...") return controller:scrollIntoView(s, "//*[@id='bottom-element']") end) :_then(function(s, _) return s:delay(1000) end) -- 等待滚动动画 :_then(function(s, _) -- 验证滚动的一种方法是检查元素现在是否可见 return controller:isDisplayed(s, "//*[@id='bottom-element']") end) :_then(function(s, isDisplayed) _console:assertTrue(isDisplayed, "滚动测试失败,元素未变得可见") _console:info("--- 示例 2.7: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.7: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.7: 键盘与滚动操作 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.7: 键盘与滚动操作 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备一个长页面用于滚动 $html = <<<HTML <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> HTML; // 3. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://scroll.test"), javaString($html), null, null, javaString("https://scroll.test")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller) { global $console; $console->log(javaString("测试按键 'Enter'...")); return $controller->pressKey($s, javaString("//*[@id='key-input']"), javaString("Enter")); }) ->then(function ($s, $unused) { return $s->delay(500); }) ->then(function ($s, $unused) use ($controller) { return $controller->getProperty($s, javaString("//*[@id='key-input']"), javaString("value")); }) ->then(function ($s, $text) use ($controller) { global $console; $console->assertTrue($text == "Enter pressed!", javaString("按键测试失败")); $console->log(javaString("测试滚动到页面底部元素...")); return $controller->scrollIntoView($s, javaString("//*[@id='bottom-element']")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待滚动动画 ->then(function ($s, $unused) use ($controller) { // 验证滚动的一种方法是检查元素现在是否可见 return $controller->isDisplayed($s, javaString("//*[@id='bottom-element']")); }) ->then(function ($s, $isDisplayed) { global $console; $console->assertTrue($isDisplayed, javaString("滚动测试失败,元素未变得可见")); $console->info(javaString("--- 示例 2.7: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.7: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.7: 键盘与滚动操作 (完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.7: 键盘与滚动操作 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备一个长页面用于滚动 html = """ <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> """ # 3. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, "https://scroll.test", html, None, None, "https://scroll.test") ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("测试按键 'Enter'..."), controller.pressKey(s, "//*[@id='key-input']", "Enter") )[-1]).then( lambda s, _: s.delay(500) ).then( lambda s, _: controller.getProperty(s, "//*[@id='key-input']", "value") ).then(lambda s, text: ( _console.assertTrue(text == "Enter pressed!", "按键测试失败"), _console.log("测试滚动到页面底部元素..."), controller.scrollIntoView(s, "//*[@id='bottom-element']") )[-1]).then( lambda s, _: s.delay(1000) ).then( lambda s, _: controller.isDisplayed(s, "//*[@id='bottom-element']") ).then(lambda s, isDisplayed: ( _console.assertTrue(isDisplayed, "滚动测试失败,元素未变得可见"), _console.info("--- 示例 2.7: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.7: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.7: 键盘与滚动操作 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.7: 键盘与滚动操作 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备一个长页面用于滚动 html = <<-HTML <html> <head><style> .spacer { height: 1500px; } </style></head> <body> <input id="key-input" onkeydown="if(event.key==='Enter') this.value='Enter pressed!'" placeholder="在此输入并按回车"> <div class="spacer"></div> <h2 id="bottom-element">页面底部</h2> </body> </html> HTML # 3. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, "https://scroll.test", html, nil, nil, "https://scroll.test") }. then { |s, _| s.delay(1000) }. then { |s, _| $console.log("测试按键 'Enter'...") controller.pressKey(s, "//*[@id='key-input']", "Enter") }. then { |s, _| s.delay(500) }. then { |s, _| controller.getProperty(s, "//*[@id='key-input']", "value") }. then { |s, text| $console.assertTrue(text == "Enter pressed!", "按键测试失败") $console.log("测试滚动到页面底部元素...") controller.scrollIntoView(s, "//*[@id='bottom-element']") }. then { |s, _| s.delay(1000) }. # 等待滚动动画 then { |s, _| # 验证滚动的一种方法是检查元素现在是否可见 controller.isDisplayed(s, "//*[@id='bottom-element']") }. then { |s, is_displayed| $console.assertTrue(is_displayed, "滚动测试失败,元素未变得可见") $console.info("--- 示例 2.7: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.7: 失败 ---", e.to_s) }

更多状态检查与属性获取

本节演示用于检查元素状态的额外方法。

  • isSelected(xpath): 检查一个表单元素(如复选框、单选按钮)是否被选中。

  • getRect(xpath): 获取元素相对于视口的位置和尺寸。

  • getElementCount(xpath): 获取匹配XPath的元素数量。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.8: 更多状态检查与属性获取 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备HTML val html = """ <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL( s, "https://state.test", html, null, null, "https://state.test" ) } .then { s, _ -> s.delay(1000) } .then { s, _ -> _console.log("检查复选框是否被选中...") controller.isSelected(s, "//*[@id='my-checkbox']") } .then { s, isSelected -> _console.assertTrue(isSelected, "isSelected 测试失败,复选框应被选中") _console.log("获取复选框的位置和尺寸...") controller.getRect(s, "//*[@id='my-checkbox']") } .then { s, rect -> _console.log("复选框 Rect:", rect) // rect 是一个 RectF 对象 _console.assertTrue(rect != null && rect.getWidth() > 0, "getRect 测试失败,宽度应大于0") _console.log("获取 'item' class 的元素数量...") controller.getElementCount(s, "//*[@class='item']") } .then { s, count -> _console.log("元素数量:", count) _console.assertTrue(count == 3, "getElementCount 测试失败,应为3") _console.info("--- 示例 2.8: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.8: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.8: 更多状态检查与属性获取 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备HTML def html = """ <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> """ // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://state.test", html, null, null, "https://state.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> $console.log("检查复选框是否被选中...") return controller.isSelected(s, "//*[@id='my-checkbox']") } .then { s, isSelected -> $console.assertTrue(isSelected, "isSelected 测试失败,复选框应被选中") $console.log("获取复选框的位置和尺寸...") return controller.getRect(s, "//*[@id='my-checkbox']") } .then { s, rect -> $console.log("复选框 Rect:", rect) // rect 是一个 RectF 对象 $console.assertTrue(rect.getWidth() > 0, "getRect 测试失败,宽度应大于0") $console.log("获取 'item' class 的元素数量...") return controller.getElementCount(s, "//*[@class='item']") } .then { s, count -> $console.log("元素数量:", count) $console.assertTrue(count == 3, "getElementCount 测试失败,应为3") $console.info("--- 示例 2.8: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.8: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.8: 更多状态检查与属性获取 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备HTML const html = ` <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> `; // 3. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, "https://state.test", html, null, null, "https://state.test")) .then((s, _) => s.delay(1000)) .then((s, _) => { $console.log("检查复选框是否被选中..."); return controller.isSelected(s, "//*[@id='my-checkbox']"); }) .then((s, isSelected) => { $console.assertTrue(isSelected == true, "isSelected 测试失败,复选框应被选中"); $console.log("获取复选框的位置和尺寸..."); return controller.getRect(s, "//*[@id='my-checkbox']"); }) .then((s, rect) => { $console.log("复选框 Rect:", rect); // rect 是一个 RectF 对象 $console.assertTrue(rect.getWidth() > 0, "getRect 测试失败,宽度应大于0"); $console.log("获取 'item' class 的元素数量..."); return controller.getElementCount(s, "//*[@class='item']"); }) .then((s, count) => { $console.log("元素数量:", count); $console.assertTrue(count == 3, "getElementCount 测试失败,应为3"); $console.info("--- 示例 2.8: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.8: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.8: 更多状态检查与属性获取 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备HTML local html = [[ <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> ]] -- 3. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://state.test", html, nil, nil, "https://state.test") end) :_then(function(s, _) return s:delay(1000) end) :_then(function(s, _) _console:log("检查复选框是否被选中...") return controller:isSelected(s, "//*[@id='my-checkbox']") end) :_then(function(s, isSelected) _console:assertTrue(isSelected, "isSelected 测试失败,复选框应被选中") _console:log("获取复选框的位置和尺寸...") return controller:getRect(s, "//*[@id='my-checkbox']") end) :_then(function(s, rect) _console:log("复选框 Rect:", rect) -- rect 是一个 RectF 对象 _console:assertTrue(rect:getWidth() > 0, "getRect 测试失败,宽度应大于0") _console:log("获取 'item' class 的元素数量...") return controller:getElementCount(s, "//*[@class='item']") end) :_then(function(s, count) _console:log("元素数量:", count) _console:assertTrue(count == 3, "getElementCount 测试失败,应为3") _console:info("--- 示例 2.8: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.8: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.8: 更多状态检查与属性获取 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备HTML $html = <<<HTML <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> HTML; // 3. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://state.test"), javaString($html), null, null, javaString("https://state.test")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller) { global $console; $console->log(javaString("检查复选框是否被选中...")); return $controller->isSelected($s, javaString("//*[@id='my-checkbox']")); }) ->then(function ($s, $isSelected) use ($controller) { global $console; $console->assertTrue($isSelected, javaString("isSelected 测试失败,复选框应被选中")); $console->log(javaString("获取复选框的位置和尺寸...")); return $controller->getRect($s, javaString("//*[@id='my-checkbox']")); }) ->then(function ($s, $rect) use ($controller) { global $console; $console->log(javaString("复选框 Rect:"), $rect); // rect 是一个 RectF 对象 $console->assertTrue($rect->getWidth() > 0, javaString("getRect 测试失败,宽度应大于0")); $console->log(javaString("获取 'item' class 的元素数量...")); return $controller->getElementCount($s, javaString("//*[@class='item']")); }) ->then(function ($s, $count) { global $console; $console->log(javaString("元素数量:"), $count); $console->assertTrue($count == 3, javaString("getElementCount 测试失败,应为3")); $console->info(javaString("--- 示例 2.8: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.8: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.8: 更多状态检查与属性获取 (完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备HTML html = """ <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> """ # 3. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, "https://state.test", html, None, None, "https://state.test") ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("检查复选框是否被选中..."), controller.isSelected(s, "//*[@id='my-checkbox']") )[-1]).then(lambda s, isSelected: ( _console.assertTrue(isSelected, "isSelected 测试失败,复选框应被选中"), _console.log("获取复选框的位置和尺寸..."), controller.getRect(s, "//*[@id='my-checkbox']") )[-1]).then(lambda s, rect: ( _console.log("复选框 Rect:", rect), _console.assertTrue(rect.getWidth() > 0, "getRect 测试失败,宽度应大于0"), _console.log("获取 'item' class 的元素数量..."), controller.getElementCount(s, "//*[@class='item']") )[-1]).then(lambda s, count: ( _console.log("元素数量:", count), _console.assertTrue(count == 3, "getElementCount 测试失败,应为3"), _console.info("--- 示例 2.8: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.8: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.8: 更多状态检查与属性获取 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.8: 更多状态检查与属性获取 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备HTML html = <<-HTML <html><body> <input type="checkbox" id="my-checkbox" checked> <ul> <li class="item">项目 1</li> <li class="item">项目 2</li> <li class="item">项目 3</li> </ul> </body></html> HTML # 3. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, "https://state.test", html, nil, nil, "https://state.test") }. then { |s, _| s.delay(1000) }. then { |s, _| $console.log("检查复选框是否被选中...") controller.isSelected(s, "//*[@id='my-checkbox']") }. then { |s, is_selected| $console.assertTrue(is_selected, "isSelected 测试失败,复选框应被选中") $console.log("获取复选框的位置和尺寸...") controller.getRect(s, "//*[@id='my-checkbox']") }. then { |s, rect| $console.log("复选框 Rect:", rect) # rect 是一个 RectF 对象 $console.assertTrue(rect.getWidth() > 0, "getRect 测试失败,宽度应大于0") $console.log("获取 'item' class 的元素数量...") controller.getElementCount(s, "//*[@class='item']") }. then { |s, count| $console.log("元素数量:", count) $console.assertTrue(count == 3, "getElementCount 测试失败,应为3") $console.info("--- 示例 2.8: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.8: 失败 ---", e.to_s) }

Iframe 上下文切换

本节演示如何在 iframe 内部和外部之间切换,以操作被嵌套的页面内容。

  • switchToFrame(xpath): 将控制器的上下文切换到指定的iframe内部。

  • switchToDefaultContent(): 将上下文从iframe切回到主页面。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.9: Iframe 上下文切换 (修正版) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.9: Iframe 上下文切换 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备带有Iframe的HTML // **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** // 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 val iframe_content = """<button id="iframe-btn">Iframe里的按钮</button>""" val html = """ <html> <body> <h1>主页面</h1> <iframe id="my-frame" srcdoc='$iframe_content'></iframe> </body> </html> """ val iframeBtnXpath = "//*[@id='iframe-btn']" // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://iframe.test", html, null, null, "https://iframe.test") } .then { s, _ -> s.delay(1000) } // 等待iframe渲染 .then { s, _ -> _console.log("切换前,尝试查找Iframe内的按钮...") controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> _console.log("切换前找到的数量:", count) _console.assertTrue(count == 0, "切换前不应找到Iframe内的元素") _console.log("切换到Iframe中...") controller.switchToFrame(s, "//*[@id='my-frame']") } .then { s, _ -> _console.log("切换后,再次尝试查找Iframe内的按钮...") controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> _console.log("切换后找到的数量:", count) // 现在应该能正确找到了 _console.assertTrue(count == 1, "切换后应找到Iframe内的元素") _console.log("切换回主内容...") controller.switchToDefaultContent(s) } .then { s, _ -> _console.log("切回后,再次尝试查找Iframe内的按钮...") controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> _console.log("切回后找到的数量:", count) _console.assertTrue(count == 0, "切回后不应找到Iframe内的元素") _console.info("--- 示例 2.9: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.9: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.9: Iframe 上下文切换 (修正版) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.9: Iframe 上下文切换 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备带有Iframe的HTML // **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** // 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 def iframe_content = """<button id="iframe-btn">Iframe里的按钮</button>""" def html = """ <html> <body> <h1>主页面</h1> <iframe id="my-frame" srcdoc='${iframe_content}'></iframe> </body> </html> """ def iframeBtnXpath = "//*[@id='iframe-btn']" // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://iframe.test", html, null, null, "https://iframe.test") } .then { s, _ -> s.delay(1000) } // 等待iframe渲染 .then { s, _ -> $console.log("切换前,尝试查找Iframe内的按钮...") return controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> $console.log("切换前找到的数量:", count) $console.assertTrue(count == 0, "切换前不应找到Iframe内的元素") $console.log("切换到Iframe中...") return controller.switchToFrame(s, "//*[@id='my-frame']") } .then { s, _ -> $console.log("切换后,再次尝试查找Iframe内的按钮...") return controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> $console.log("切换后找到的数量:", count) // 现在应该能正确找到了 $console.assertTrue(count == 1, "切换后应找到Iframe内的元素") $console.log("切换回主内容...") return controller.switchToDefaultContent(s) } .then { s, _ -> $console.log("切回后,再次尝试查找Iframe内的按钮...") return controller.getElementCount(s, iframeBtnXpath) } .then { s, count -> $console.log("切回后找到的数量:", count) $console.assertTrue(count == 0, "切回后不应找到Iframe内的元素") $console.info("--- 示例 2.9: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.9: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.9: Iframe 上下文切换 (修正版) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.9: Iframe 上下文切换 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备带有Iframe的HTML // **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** // 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 const iframe_content = `<button id="iframe-btn">Iframe里的按钮</button>`; const html = ` <html> <body> <h1>主页面</h1> <iframe id="my-frame" srcdoc='${iframe_content}'></iframe> </body> </html> `; const iframeBtnXpath = "//*[@id='iframe-btn']"; // 3. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, "https://iframe.test", html, null, null, "https://iframe.test")) .then((s, _) => s.delay(1000)) // 等待iframe渲染 .then((s, _) => { $console.log("切换前,尝试查找Iframe内的按钮..."); return controller.getElementCount(s, iframeBtnXpath); }) .then((s, count) => { $console.log("切换前找到的数量:", count); $console.assertTrue(count == 0, "切换前不应找到Iframe内的元素"); $console.log("切换到Iframe中..."); return controller.switchToFrame(s, "//*[@id='my-frame']"); }) .then((s, _) => { $console.log("切换后,再次尝试查找Iframe内的按钮..."); return controller.getElementCount(s, iframeBtnXpath); }) .then((s, count) => { $console.log("切换后找到的数量:", count); // 现在应该能正确找到了 $console.assertTrue(count == 1, "切换后应找到Iframe内的元素"); $console.log("切换回主内容..."); return controller.switchToDefaultContent(s); }) .then((s, _) => { $console.log("切回后,再次尝试查找Iframe内的按钮..."); return controller.getElementCount(s, iframeBtnXpath); }) .then((s, count) => { $console.log("切回后找到的数量:", count); $console.assertTrue(count == 0, "切回后不应找到Iframe内的元素"); $console.info("--- 示例 2.9: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.9: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.9: Iframe 上下文切换 (修正版) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.9: Iframe 上下文切换 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备带有Iframe的HTML -- **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** -- 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 local iframe_content = [[<button id="iframe-btn">Iframe里的按钮</button>]] local html = "<html>\n <body>\n <h1>主页面</h1>\n <iframe id=\"my-frame\" srcdoc='" .. iframe_content .. "'></iframe>\n </body>\n </html>" local iframeBtnXpath = "//*[@id='iframe-btn']" -- 3. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://iframe.test", html, nil, nil, "https://iframe.test") end) :_then(function(s, _) return s:delay(1000) end) -- 等待iframe渲染 :_then(function(s, _) _console:log("切换前,尝试查找Iframe内的按钮...") return controller:getElementCount(s, iframeBtnXpath) end) :_then(function(s, count) _console:log("切换前找到的数量:", count) _console:assertTrue(count == 0, "切换前不应找到Iframe内的元素") _console:log("切换到Iframe中...") return controller:switchToFrame(s, "//*[@id='my-frame']") end) :_then(function(s, _) _console:log("切换后,再次尝试查找Iframe内的按钮...") return controller:getElementCount(s, iframeBtnXpath) end) :_then(function(s, count) _console:log("切换后找到的数量:", count) -- 现在应该能正确找到了 _console:assertTrue(count == 1, "切换后应找到Iframe内的元素") _console:log("切换回主内容...") return controller:switchToDefaultContent(s) end) :_then(function(s, _) _console:log("切回后,再次尝试查找Iframe内的按钮...") return controller:getElementCount(s, iframeBtnXpath) end) :_then(function(s, count) _console:log("切回后找到的数量:", count) _console:assertTrue(count == 0, "切回后不应找到Iframe内的元素") _console:info("--- 示例 2.9: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.9: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.9: Iframe 上下文切换 (修正版) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.9: Iframe 上下文切换 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备带有Iframe的HTML // **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** // 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 $iframe_content = "<button id=\"iframe-btn\">Iframe里的按钮</button>"; $html = " <html> <body> <h1>主页面</h1> <iframe id=\"my-frame\" srcdoc='" . $iframe_content . "'></iframe> </body> </html> "; $iframeBtnXpath = "//*[@id='iframe-btn']"; // 3. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://iframe.test"), javaString($html), null, null, javaString("https://iframe.test")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // 等待iframe渲染 ->then(function ($s, $unused) use ($controller, $iframeBtnXpath) { global $console; $console->log(javaString("切换前,尝试查找Iframe内的按钮...")); return $controller->getElementCount($s, $iframeBtnXpath); }) ->then(function ($s, $count) use ($controller) { global $console; $console->log(javaString("切换前找到的数量:"), $count); $console->assertTrue($count == 0, javaString("切换前不应找到Iframe内的元素")); $console->log(javaString("切换到Iframe中...")); return $controller->switchToFrame($s, javaString("//*[@id='my-frame']")); }) ->then(function ($s, $unused) use ($controller, $iframeBtnXpath) { global $console; $console->log(javaString("切换后,再次尝试查找Iframe内的按钮...")); return $controller->getElementCount($s, $iframeBtnXpath); }) ->then(function ($s, $count) use ($controller) { global $console; $console->log(javaString("切换后找到的数量:"), $count); // 现在应该能正确找到了 $console->assertTrue($count == 1, javaString("切换后应找到Iframe内的元素")); $console->log(javaString("切换回主内容...")); return $controller->switchToDefaultContent($s); }) ->then(function ($s, $unused) use ($controller, $iframeBtnXpath) { global $console; $console->log(javaString("切回后,再次尝试查找Iframe内的按钮...")); return $controller->getElementCount($s, $iframeBtnXpath); }) ->then(function ($s, $count) { global $console; $console->log(javaString("切回后找到的数量:"), $count); $console->assertTrue($count == 0, javaString("切回后不应找到Iframe内的元素")); $console->info(javaString("--- 示例 2.9: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.9: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.9: Iframe 上下文切换 (修正版) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.9: Iframe 上下文切换 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备带有Iframe的HTML iframe_content = """<button id="iframe-btn">Iframe里的按钮</button>""" # Use Python's f-string for string interpolation html = f""" <html> <body> <h1>主页面</h1> <iframe id="my-frame" srcdoc='{iframe_content}'></iframe> </body> </html> """ iframeBtnXpath = "//*[@id='iframe-btn']" # 3. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, "https://iframe.test", html, None, None, "https://iframe.test") ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("切换前,尝试查找Iframe内的按钮..."), controller.getElementCount(s, iframeBtnXpath) )[-1]).then(lambda s, count: ( _console.log("切换前找到的数量:", count), _console.assertTrue(count == 0, "切换前不应找到Iframe内的元素"), _console.log("切换到Iframe中..."), controller.switchToFrame(s, "//*[@id='my-frame']") )[-1]).then(lambda s, _: ( _console.log("切换后,再次尝试查找Iframe内的按钮..."), controller.getElementCount(s, iframeBtnXpath) )[-1]).then(lambda s, count: ( _console.log("切换后找到的数量:", count), _console.assertTrue(count == 1, "切换后应找到Iframe内的元素"), _console.log("切换回主内容..."), controller.switchToDefaultContent(s) )[-1]).then(lambda s, _: ( _console.log("切回后,再次尝试查找Iframe内的按钮..."), controller.getElementCount(s, iframeBtnXpath) )[-1]).then(lambda s, count: ( _console.log("切回后找到的数量:", count), _console.assertTrue(count == 0, "切回后不应找到Iframe内的元素"), _console.info("--- 示例 2.9: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.9: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.9: Iframe 上下文切换 (修正版) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.9: Iframe 上下文切换 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备带有Iframe的HTML # **核心修正:使用 srcdoc 属性代替 src="data:..." 来确保同源** # 注意:iframe_content 内部的属性使用双引号,以便 srcdoc 可以用单引号包围 iframe_content = "<button id=\"iframe-btn\">Iframe里的按钮</button>" html = <<-HTML <html> <body> <h1>主页面</h1> <iframe id="my-frame" srcdoc='#{iframe_content}'></iframe> </body> </html> HTML iframe_btn_xpath = "//*[@id='iframe-btn']" # 3. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, "https://iframe.test", html, nil, nil, "https://iframe.test") }. then { |s, _| s.delay(1000) }. # 等待iframe渲染 then { |s, _| $console.log("切换前,尝试查找Iframe内的按钮...") controller.getElementCount(s, iframe_btn_xpath) }. then { |s, count| $console.log("切换前找到的数量:", count) $console.assertTrue(count == 0, "切换前不应找到Iframe内的元素") $console.log("切换到Iframe中...") controller.switchToFrame(s, "//*[@id='my-frame']") }. then { |s, _| $console.log("切换后,再次尝试查找Iframe内的按钮...") controller.getElementCount(s, iframe_btn_xpath) }. then { |s, count| $console.log("切换后找到的数量:", count) # 现在应该能正确找到了 $console.assertTrue(count == 1, "切换后应找到Iframe内的元素") $console.log("切换回主内容...") controller.switchToDefaultContent(s) }. then { |s, _| $console.log("切回后,再次尝试查找Iframe内的按钮...") controller.getElementCount(s, iframe_btn_xpath) }. then { |s, count| $console.log("切回后找到的数量:", count) $console.assertTrue(count == 0, "切回后不应找到Iframe内的元素") $console.info("--- 示例 2.9: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.9: 失败 ---", e.to_s) }

本节展示如何管理浏览器的 Cookie, LocalStorage 和 SessionStorage。

  • addCookie(name, value, domain, path)

  • getCookie(name) / getAllCookies()

  • deleteCookie(name) / deleteAllCookies()

  • set/get/removeLocalStorageItem(key, value)

  • clearLocalStorage()

  • set/get/removeSessionStorageItem(key, value)

  • clearSessionStorage()

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.10: Cookie 与本地存储管理 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备HTML val html = "<html><body>存储测试页面</body></html>" val domain = "storage.test" val url = "https://$domain" // 3. 开始异步操作链 scope.delay(1000) .then { s, _: Any? -> controller.loadDataWithBaseURL(s, url, html, null, null, url) } .then { s, _: Any? -> s.delay(1000) } // --- Cookie 测试 --- .then { s, _: Any? -> _console.log("添加Cookie: 'user=M8Test'...") controller.addCookie(s, "user", "M8Test", domain, "/") } .then { s, _: Any? -> controller.getCookie(s, "user") } .then { s, cookieValue -> _console.log("获取到的Cookie 'user':", cookieValue) // cookieValue 应该是 String,这里加个空安全处理防止 null _console.assertTrue(cookieValue?.toString() == "M8Test", "Cookie添加或获取失败") controller.deleteAllCookies(s) } .then { s, _: Any? -> controller.getAllCookies(s) } .then { s, cookies -> _console.log("删除所有Cookie后:", cookies) _console.assertTrue(cookies.isEmpty(), "删除所有Cookie失败") // --- LocalStorage 测试 --- controller.setLocalStorageItem(s, "theme", "dark") } .then { s, _: Any? -> controller.getLocalStorageItem(s, "theme") } .then { s, theme -> _console.log("获取到的LocalStorage 'theme':", theme) _console.assertTrue(theme == "dark", "LocalStorage设置或获取失败") controller.clearLocalStorage(s) } .then { s, _: Any? -> controller.getLocalStorageItem(s, "theme") } .then { s, theme -> _console.log("清空LocalStorage后 'theme':", theme) _console.assertTrue(theme == null, "清空LocalStorage失败") _console.info("--- 示例 2.10: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.10: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.10: Cookie 与本地存储管理 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备HTML def html = "<html><body>存储测试页面</body></html>" def domain = "storage.test" def url = "https://${domain}" // 3. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, url, html, null, null, url) } .then { s, _ -> s.delay(1000) } // --- Cookie 测试 --- .then { s, _ -> $console.log("添加Cookie: 'user=M8Test'...") return controller.addCookie(s, "user", "M8Test", domain, "/") } .then { s, _ -> controller.getCookie(s, "user") } .then { s, cookieValue -> $console.log("获取到的Cookie 'user':", cookieValue) $console.assertTrue(cookieValue == "M8Test", "Cookie添加或获取失败") return controller.deleteAllCookies(s) } .then { s, _ -> controller.getAllCookies(s) } .then { s, cookies -> $console.log("删除所有Cookie后:", cookies) $console.assertTrue(cookies.isEmpty(), "删除所有Cookie失败") // --- LocalStorage 测试 --- return controller.setLocalStorageItem(s, "theme", "dark") } .then { s, _ -> controller.getLocalStorageItem(s, "theme") } .then { s, theme -> $console.log("获取到的LocalStorage 'theme':", theme) $console.assertTrue(theme == "dark", "LocalStorage设置或获取失败") return controller.clearLocalStorage(s) } .then { s, _ -> controller.getLocalStorageItem(s, "theme") } .then { s, theme -> $console.log("清空LocalStorage后 'theme':", theme) $console.assertTrue(theme == null, "清空LocalStorage失败") $console.info("--- 示例 2.10: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.10: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.10: Cookie 与本地存储管理 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备HTML const html = "<html><body>存储测试页面</body></html>"; const domain = "storage.test"; const url = `https://${domain}`; // 3. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, url, html, null, null, url)) .then((s, _) => s.delay(1000)) // --- Cookie 测试 --- .then((s, _) => { $console.log("添加Cookie: 'user=M8Test'..."); return controller.addCookie(s, "user", "M8Test", domain, "/"); }) .then((s, _) => controller.getCookie(s, "user")) .then((s, cookieValue) => { $console.log("获取到的Cookie 'user':", cookieValue); $console.assertTrue(cookieValue == "M8Test", "Cookie添加或获取失败"); return controller.deleteAllCookies(s); }) .then((s, _) => controller.getAllCookies(s)) .then((s, cookies) => { $console.log("删除所有Cookie后:", cookies); $console.assertTrue(cookies.isEmpty(), "删除所有Cookie失败"); // --- LocalStorage 测试 --- return controller.setLocalStorageItem(s, "theme", "dark"); }) .then((s, _) => controller.getLocalStorageItem(s, "theme")) .then((s, theme) => { $console.log("获取到的LocalStorage 'theme':", theme); $console.assertTrue(theme == "dark", "LocalStorage设置或获取失败"); return controller.clearLocalStorage(s); }) .then((s, _) => controller.getLocalStorageItem(s, "theme")) .then((s, theme) => { $console.log("清空LocalStorage后 'theme':", theme); $console.assertTrue(theme == null, "清空LocalStorage失败"); $console.info("--- 示例 2.10: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.10: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.10: Cookie 与本地存储管理 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备HTML local html = "<html><body>存储测试页面</body></html>" local domain = "storage.test" local url = "https://" .. domain -- 3. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, url, html, nil, nil, url) end) :_then(function(s, _) return s:delay(1000) end) -- --- Cookie 测试 --- :_then(function(s, _) _console:log("添加Cookie: 'user=M8Test'...") return controller:addCookie(s, "user", "M8Test", domain, "/") end) :_then(function(s, _) return controller:getCookie(s, "user") end) :_then(function(s, cookieValue) _console:log("获取到的Cookie 'user':", cookieValue) _console:assertTrue(cookieValue == "M8Test", "Cookie添加或获取失败") return controller:deleteAllCookies(s) end) :_then(function(s, _) return controller:getAllCookies(s) end) :_then(function(s, cookies) _console:log("删除所有Cookie后:", cookies) _console:assertTrue(cookies:isEmpty(), "删除所有Cookie失败") -- --- LocalStorage 测试 --- return controller:setLocalStorageItem(s, "theme", "dark") end) :_then(function(s, _) return controller:getLocalStorageItem(s, "theme") end) :_then(function(s, theme) _console:log("获取到的LocalStorage 'theme':", theme) _console:assertTrue(theme == "dark", "LocalStorage设置或获取失败") return controller:clearLocalStorage(s) end) :_then(function(s, _) return controller:getLocalStorageItem(s, "theme") end) :_then(function(s, theme) _console:log("清空LocalStorage后 'theme':", theme) _console:assertTrue(theme == nil, "清空LocalStorage失败") _console:info("--- 示例 2.10: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.10: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; // ---------------------------------------------------- // 示例 2.10: Cookie 与本地存储管理 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备HTML $html = "<html><body>存储测试页面</body></html>"; $domain = "storage.test"; $url = "https://" . $domain; // 3. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $url, $html) { return $controller->loadDataWithBaseURL($s, javaString($url), javaString($html), null, null, javaString($url)); }) ->then(function ($s, $unused) { return $s->delay(1000); }) // --- Cookie 测试 --- ->then(function ($s, $unused) use ($controller, $domain) { global $console; $console->log(javaString("添加Cookie: 'user=M8Test'...")); return $controller->addCookie($s, javaString("user"), javaString("M8Test"), javaString($domain), javaString("/")); }) ->then(function ($s, $unused) use ($controller) { return $controller->getCookie($s, javaString("user")); }) ->then(function ($s, $cookieValue) use ($controller) { global $console; $console->log(javaString("获取到的Cookie 'user':"), $cookieValue); $console->assertTrue($cookieValue == "M8Test", javaString("Cookie添加或获取失败")); return $controller->deleteAllCookies($s); }) ->then(function ($s, $unused) use ($controller) { return $controller->getAllCookies($s); }) ->then(function ($s, $cookies) use ($controller) { global $console; $console->log(javaString("删除所有Cookie后:"), $cookies); $console->assertTrue(empty($cookies), javaString("删除所有Cookie失败")); // --- LocalStorage 测试 --- return $controller->setLocalStorageItem($s, javaString("theme"), javaString("dark")); }) ->then(function ($s, $unused) use ($controller) { return $controller->getLocalStorageItem($s, javaString("theme")); }) ->then(function ($s, $theme) use ($controller) { global $console; $console->log(javaString("获取到的LocalStorage 'theme':"), $theme); $console->assertTrue($theme == "dark", javaString("LocalStorage设置或获取失败")); return $controller->clearLocalStorage($s); }) ->then(function ($s, $unused) use ($controller) { return $controller->getLocalStorageItem($s, javaString("theme")); }) ->then(function ($s, $theme) { global $console; $console->log(javaString("清空LocalStorage后 'theme':"), $theme); $console->assertTrue($theme == null, javaString("清空LocalStorage失败")); $console->info(javaString("--- 示例 2.10: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.10: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.10: Cookie 与本地存储管理 (完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity _console.info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备HTML html = "<html><body>存储测试页面</body></html>" domain = "storage.test" url = f"https://{domain}" # 3. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, url, html, None, None, url) ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("添加Cookie: 'user=M8Test'..."), controller.addCookie(s, "user", "M8Test", domain, "/") )[-1]).then( lambda s, _: controller.getCookie(s, "user") ).then(lambda s, cookieValue: ( _console.log("获取到的Cookie 'user':", cookieValue), _console.assertTrue(cookieValue == "M8Test", "Cookie添加或获取失败"), controller.deleteAllCookies(s) )[-1]).then( lambda s, _: controller.getAllCookies(s) ).then(lambda s, cookies: ( _console.log("删除所有Cookie后:", cookies), _console.assertTrue(not cookies, "删除所有Cookie失败"), controller.setLocalStorageItem(s, "theme", "dark") )[-1]).then( lambda s, _: controller.getLocalStorageItem(s, "theme") ).then(lambda s, theme: ( _console.log("获取到的LocalStorage 'theme':", theme), _console.assertTrue(theme == "dark", "LocalStorage设置或获取失败"), controller.clearLocalStorage(s) )[-1]).then( lambda s, _: controller.getLocalStorageItem(s, "theme") ).then(lambda s, theme: ( _console.log("清空LocalStorage后 'theme':", theme), _console.assertTrue(theme is None, "清空LocalStorage失败"), _console.info("--- 示例 2.10: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.10: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.10: Cookie 与本地存储管理 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.10: Cookie 与本地存储管理 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备HTML html = "<html><body>存储测试页面</body></html>" domain = "storage.test" url = "https://#{domain}" # 3. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, url, html, nil, nil, url) }. then { |s, _| s.delay(1000) }. # --- Cookie 测试 --- then { |s, _| $console.log("添加Cookie: 'user=M8Test'...") controller.addCookie(s, "user", "M8Test", domain, "/") }. then { |s, _| controller.getCookie(s, "user") }. then { |s, cookie_value| $console.log("获取到的Cookie 'user':", cookie_value) $console.assertTrue(cookie_value == "M8Test", "Cookie添加或获取失败") controller.deleteAllCookies(s) }. then { |s, _| controller.getAllCookies(s) }. then { |s, cookies| $console.log("删除所有Cookie后:", cookies) $console.assertTrue(cookies.isEmpty(), "删除所有Cookie失败") # --- LocalStorage 测试 --- controller.setLocalStorageItem(s, "theme", "dark") }. then { |s, _| controller.getLocalStorageItem(s, "theme") }. then { |s, theme| $console.log("获取到的LocalStorage 'theme':", theme) $console.assertTrue(theme == "dark", "LocalStorage设置或获取失败") controller.clearLocalStorage(s) }. then { |s, _| controller.getLocalStorageItem(s, "theme") }. then { |s, theme| $console.log("清空LocalStorage后 'theme':", theme) $console.assertTrue(theme.nil?, "清空LocalStorage失败") $console.info("--- 示例 2.10: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.10: 失败 ---", e.to_s) }

截图功能

本节演示如何对整个WebView或特定元素进行截图。

  • takeScreenshot(file): 截取WebView可视区域的完整图像。

  • takeElementScreenshot(xpath, file): 仅截取指定元素的图像。

import com.m8test.script.GlobalVariables.* // ---------------------------------------------------- // 示例 2.11: 截图功能 (完整代码) // ---------------------------------------------------- _console.info("\n--- 开始示例 2.11: 截图功能 ---") // 1. 基础设置 val scope = _coroutines.newScope { setDispatcher { it.getIO() } } val bridge = _webView.getBridge() val controller = bridge.getController() _activity.start() // 2. 准备HTML val html = """ <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> """ // 3. 准备用于保存截图的文件 val fullScreenshotFile = _files.buildFile { setPath("/sdcard/M8Test/tmp", "full_shot.png") } fullScreenshotFile.mkdirs() fullScreenshotFile.deleteRecursively() val elementScreenshotFile = _files.buildFile { setPath("/sdcard/M8Test/tmp", "ele_shot.png") } elementScreenshotFile.mkdirs() elementScreenshotFile.deleteRecursively() // 4. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://screenshot.test", html, null, null, "https://screenshot.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> _console.log("进行整页截图...") controller.takeScreenshot(s, fullScreenshotFile) } .then { s, success -> _console.assertTrue(success, "整页截图失败") _console.log("整页截图已保存到:", fullScreenshotFile.getPath()) _console.log("进行元素截图...") controller.takeElementScreenshot(s, "//*[@id='highlight']", elementScreenshotFile) } .then { s, success -> _console.assertTrue(success, "元素截图失败") _console.log("元素截图已保存到:", elementScreenshotFile.getPath()) _console.info("--- 示例 2.11: 成功 ---") s.resolve(null) } .onError { e -> _console.error("--- 示例 2.11: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.11: 截图功能 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.11: 截图功能 ---") // 1. 基础设置 def scope = $coroutines.newScope { it.setDispatcher { it.getIO() } } def bridge = $webView.getBridge() def controller = bridge.getController() $activity.start() // 2. 准备HTML def html = """ <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> """ // 3. 准备用于保存截图的文件 def fullScreenshotFile = $files.buildFile { fb -> fb.setPath("/sdcard/M8Test/tmp", "full_shot.png") } fullScreenshotFile.mkdirs() fullScreenshotFile.deleteRecursively() def elementScreenshotFile = $files.buildFile { fb -> fb.setPath("/sdcard/M8Test/tmp", "ele_shot.png") } elementScreenshotFile.mkdirs() elementScreenshotFile.deleteRecursively() // 4. 开始异步操作链 scope.delay(1000) .then { s, _ -> controller.loadDataWithBaseURL(s, "https://screenshot.test", html, null, null, "https://screenshot.test") } .then { s, _ -> s.delay(1000) } .then { s, _ -> $console.log("进行整页截图...") return controller.takeScreenshot(s, fullScreenshotFile) } .then { s, success -> $console.assertTrue(success, "整页截图失败") $console.log("整页截图已保存到:", fullScreenshotFile.getPath()) $console.log("进行元素截图...") return controller.takeElementScreenshot(s, "//*[@id='highlight']", elementScreenshotFile) } .then { s, success -> $console.assertTrue(success, "元素截图失败") $console.log("元素截图已保存到:", elementScreenshotFile.getPath()) $console.info("--- 示例 2.11: 成功 ---") return s.resolve(null) } .onError { e -> $console.error("--- 示例 2.11: 失败 ---", e.stackTraceToString()) }
// ---------------------------------------------------- // 示例 2.11: 截图功能 (完整代码) // ---------------------------------------------------- $console.info("\n--- 开始示例 2.11: 截图功能 ---"); // 1. 基础设置 const scope = $coroutines.newScope(it => it.setDispatcher(it => it.getIO())); const bridge = $webView.getBridge(); const controller = bridge.getController(); $activity.start(); // 2. 准备HTML const html = ` <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> `; // 3. 准备用于保存截图的文件 const fullScreenshotFile = $files.buildFile(fb => { fb.setPath("/sdcard/M8Test/tmp", "full_shot.png"); }); fullScreenshotFile.mkdirs(); fullScreenshotFile.deleteRecursively(); const elementScreenshotFile = $files.buildFile(fb => { fb.setPath("/sdcard/M8Test/tmp", "ele_shot.png"); }); elementScreenshotFile.mkdirs(); elementScreenshotFile.deleteRecursively(); // 4. 开始异步操作链 scope.delay(1000) .then((s, _) => controller.loadDataWithBaseURL(s, "https://screenshot.test", html, null, null, "https://screenshot.test")) .then((s, _) => s.delay(1000)) .then((s, _) => { $console.log("进行整页截图..."); return controller.takeScreenshot(s, fullScreenshotFile); }) .then((s, success) => { $console.assertTrue(success == true, "整页截图失败"); $console.log("整页截图已保存到:", fullScreenshotFile.getPath()); $console.log("进行元素截图..."); return controller.takeElementScreenshot(s, "//*[@id='highlight']", elementScreenshotFile); }) .then((s, success) => { $console.assertTrue(success == true, "元素截图失败"); $console.log("元素截图已保存到:", elementScreenshotFile.getPath()); $console.info("--- 示例 2.11: 成功 ---"); return s.resolve(null); }) .onError(e => $console.error("--- 示例 2.11: 失败 ---", e.stackTraceToString()));
-- ---------------------------------------------------- -- 示例 2.11: 截图功能 (完整代码) -- ---------------------------------------------------- _console:info("\n--- 开始示例 2.11: 截图功能 ---") -- 1. 基础设置 local scope = _coroutines:newScope(function(it) it:setDispatcher(function(it) return it:getIO() end) end) local bridge = _webView:getBridge() local controller = bridge:getController() _activity:start() -- 2. 准备HTML local html = [[ <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> ]] -- 3. 准备用于保存截图的文件 local fullScreenshotFile = _files:buildFile(function(fb) fb:setPath("/sdcard/M8Test/tmp", "full_shot.png") end) fullScreenshotFile:mkdirs() fullScreenshotFile:deleteRecursively() local elementScreenshotFile = _files:buildFile(function(fb) fb:setPath("/sdcard/M8Test/tmp", "ele_shot.png") end) elementScreenshotFile:mkdirs() elementScreenshotFile:deleteRecursively() -- 4. 开始异步操作链 scope:delay(1000) :_then(function(s, _) return controller:loadDataWithBaseURL(s, "https://screenshot.test", html, nil, nil, "https://screenshot.test") end) :_then(function(s, _) return s:delay(1000) end) :_then(function(s, _) _console:log("进行整页截图...") return controller:takeScreenshot(s, fullScreenshotFile) end) :_then(function(s, success) _console:assertTrue(success, "整页截图失败") _console:log("整页截图已保存到:", fullScreenshotFile:getPath()) _console:log("进行元素截图...") return controller:takeElementScreenshot(s, "//*[@id='highlight']", elementScreenshotFile) end) :_then(function(s, success) _console:assertTrue(success, "元素截图失败") _console:log("元素截图已保存到:", elementScreenshotFile:getPath()) _console:info("--- 示例 2.11: 成功 ---") return s:resolve(nil) end) :onError(function(e) _console:error("--- 示例 2.11: 失败 ---", e:stackTraceToString()) end)
<?php /** @var m8test_java\com\m8test\script\core\api\console\Console $console */ global $console; /** @var m8test_java\com\m8test\script\core\api\coroutines\Coroutines $coroutines */ global $coroutines; /** @var m8test_java\com\m8test\script\core\api\ui\webview\WebView $webView */ global $webView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; /** @var m8test_java\com\m8test\script\core\api\file\Files $files */ global $files; // ---------------------------------------------------- // 示例 2.11: 截图功能 (完整代码) // ---------------------------------------------------- $console->info(javaString("\n--- 开始示例 2.11: 截图功能 ---")); // 1. 基础设置 $scope = $coroutines->newScope(function ($it) { $it->setDispatcher(function ($dispatchers) { return $dispatchers->getScriptMain(); }); }); $bridge = $webView->getBridge(); $controller = $bridge->getController(); $activity->start(); // 2. 准备HTML $html = <<<HTML <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> HTML; // 3. 准备用于保存截图的文件 $fullScreenshotFile = $files->buildFile(function ($fb) { $fb->setPath(javaString("/sdcard/M8Test/tmp"), javaString("full_shot.png")); }); $fullScreenshotFile->mkdirs(); $fullScreenshotFile->deleteRecursively(); $elementScreenshotFile = $files->buildFile(function ($fb) { $fb->setPath(javaString("/sdcard/M8Test/tmp"), javaString("ele_shot.png")); }); $elementScreenshotFile->mkdirs(); $elementScreenshotFile->deleteRecursively(); // 4. 开始异步操作链 $scope->delay(1000) ->then(function ($s, $unused) use ($controller, $html) { return $controller->loadDataWithBaseURL($s, javaString("https://screenshot.test"), javaString($html), null, null, javaString("https://screenshot.test")); }) ->then(function ($s, $unused) { return $s->delay(1000); }) ->then(function ($s, $unused) use ($controller, $fullScreenshotFile) { global $console; $console->log(javaString("进行整页截图...")); return $controller->takeScreenshot($s, $fullScreenshotFile); }) ->then(function ($s, $success) use ($controller, $fullScreenshotFile, $elementScreenshotFile) { global $console; $console->assertTrue($success, javaString("整页截图失败")); $console->log(javaString("整页截图已保存到:"), $fullScreenshotFile->getPath()); $console->log(javaString("进行元素截图...")); return $controller->takeElementScreenshot($s, javaString("//*[@id='highlight']"), $elementScreenshotFile); }) ->then(function ($s, $success) use ($elementScreenshotFile) { global $console; $console->assertTrue($success, javaString("元素截图失败")); $console->log(javaString("元素截图已保存到:"), $elementScreenshotFile->getPath()); $console->info(javaString("--- 示例 2.11: 成功 ---")); return $s->resolve(null); }) ->onError(function ($e) { global $console; $console->error(javaString("--- 示例 2.11: 失败 ---"), $e->stackTraceToString()); });
# # 示例 2.11: 截图功能 (完整代码) # # 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _console from m8test_java.com.m8test.script.GlobalVariables import _coroutines from m8test_java.com.m8test.script.GlobalVariables import _webView from m8test_java.com.m8test.script.GlobalVariables import _activity from m8test_java.com.m8test.script.GlobalVariables import _files _console.info("\n--- 开始示例 2.11: 截图功能 ---") # 1. 基础设置 scope = _coroutines.newScope(lambda it: it.setDispatcher(lambda d: d.getScriptMain())) bridge = _webView.getBridge() controller = bridge.getController() _activity.start() # 2. 准备HTML html = """ <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> """ # 3. 准备用于保存截图的文件 fullScreenshotFile = _files.buildFile(lambda fb: fb.setPath("/sdcard/M8Test/tmp", "full_shot.png") ) fullScreenshotFile.mkdirs() fullScreenshotFile.deleteRecursively() elementScreenshotFile = _files.buildFile(lambda fb: fb.setPath("/sdcard/M8Test/tmp", "ele_shot.png") ) elementScreenshotFile.mkdirs() elementScreenshotFile.deleteRecursively() # 4. 开始异步操作链 scope.delay(1000).then( lambda s, _: controller.loadDataWithBaseURL(s, "https://screenshot.test", html, None, None, "https://screenshot.test") ).then( lambda s, _: s.delay(1000) ).then(lambda s, _: ( _console.log("进行整页截图..."), controller.takeScreenshot(s, fullScreenshotFile) )[-1]).then(lambda s, success: ( _console.assertTrue(success, "整页截图失败"), _console.log("整页截图已保存到:", fullScreenshotFile.getPath()), _console.log("进行元素截图..."), controller.takeElementScreenshot(s, "//*[@id='highlight']", elementScreenshotFile) )[-1]).then(lambda s, success: ( _console.assertTrue(success, "元素截图失败"), _console.log("元素截图已保存到:", elementScreenshotFile.getPath()), _console.info("--- 示例 2.11: 成功 ---"), s.resolve(None) )[-1]).onError( lambda e: _console.error("--- 示例 2.11: 失败 ---", e.stackTraceToString()) )
# encoding: utf-8 # ---------------------------------------------------- # 示例 2.11: 截图功能 (完整代码) # ---------------------------------------------------- $console.info("\n--- 开始示例 2.11: 截图功能 ---") # 1. 基础设置 scope = $coroutines.newScope { |it| it.setDispatcher { |d| d.getIO() } } bridge = $webView.getBridge() controller = bridge.getController() $activity.start() # 2. 准备HTML html = <<-HTML <html> <head><style> #highlight { border: 2px solid red; padding: 10px; background-color: lightyellow; } </style></head> <body> <h1>截图测试</h1> <p>这是一个段落。</p> <div id="highlight">这是要被单独截图的元素</div> </body> </html> HTML # 3. 准备用于保存截图的文件 full_screenshot_file = $files.buildFile { |fb| fb.setPath("/sdcard/M8Test/tmp", "full_shot.png") } full_screenshot_file.mkdirs() full_screenshot_file.deleteRecursively() element_screenshot_file = $files.buildFile { |fb| fb.setPath("/sdcard/M8Test/tmp", "ele_shot.png") } element_screenshot_file.mkdirs() element_screenshot_file.deleteRecursively() # 4. 开始异步操作链 scope.delay(1000). then { |s, _| controller.loadDataWithBaseURL(s, "https://screenshot.test", html, nil, nil, "https://screenshot.test") }. then { |s, _| s.delay(1000) }. then { |s, _| $console.log("进行整页截图...") controller.takeScreenshot(s, full_screenshot_file) }. then { |s, success| $console.assertTrue(success, "整页截图失败") $console.log("整页截图已保存到:", full_screenshot_file.getPath()) $console.log("进行元素截图...") controller.takeElementScreenshot(s, "//*[@id='highlight']", element_screenshot_file) }. then { |s, success| $console.assertTrue(success, "元素截图失败") $console.log("元素截图已保存到:", element_screenshot_file.getPath()) $console.info("--- 示例 2.11: 成功 ---") s.resolve(nil) }. onError { |e| $console.error("--- 示例 2.11: 失败 ---", e.to_s) }
09 December 2025