Repository: ctripcorp/flybirds (language: python)

Issue:
支持设置浏览器语言环境
**Is your feature request related to a problem? Please describe.**
支持在flybirds_config.json中设置browser locale

**Describe the solution you'd like**
同一个定位方式, 在不同的语言环境下可能会导致定位表现不一致

**Describe alternatives you've considered**
希望能通过配置文件自定义locale
```
context = browser.new_context(record_video_dir="videos",
                                      ignore_https_errors=True,
                                      locale="zh-CN")
```




Discussion / hints:

Fix patch:
diff --git a/docs/casedsl.md b/docs/casedsl.md
index 078f2756..393a26ae 100644
--- a/docs/casedsl.md
+++ b/docs/casedsl.md
@@ -7,49 +7,59 @@ The "[]" in the statement template needs to add a value, otherwise the statement
 
 ## Common statement template
 
-| statement template                          | description                                                  | suitable for   |
-| ------------------------------------------- | ------------------------------------------------------------ | -------------- |
-| go to url[]                                 | jump to the specified url address                            | android 、web  |
-| wait [] seconds                             | Wait for a while                                             | ALL            |
-| element [] appear after page rendering      | check whether the specified element has been rendered when entering a new page | ALL            |
-| click[]                                     | click on the element with the specified attribute            | ALL            |
-| click text[]                                | Click on the element of the specified text                   | ALL            |
-| click position[][]                          | click to specify the location on the screen                  | ALL            |
-| input [] into []                            | input a string in the specified selector                     | ALL            |
-| clear [] and input[]                        | clear and input a string in the specified selector           | Web            |
-| from [] find [] element                     | find the element with the specified attribute in the specified direction | ALL            |
-| slide to [] distance []                     | swipe the specified distance in the specified direction on the full screen | ALL            |
-| [] slide to [] distance []                  | slide the specified distance in the specified direction in the specified area | ALL            |
-| exist text []                               | check that the specified string exists in the page           | ALL            |
-| not exist text []                           | check that the specified string does not exist in the page   | ALL            |
-| exist element []                            | check the presence of elements with specified attributes on the page | ALL            |
-| not exist element []                        | check that there is no element with the specified attribute in the page | ALL            |
-| element [] disappear                        | check that the element with the specified attribute in the page disappears within the specified time | App            |
-| text[]disappear                             | check that the specified string in the page disappears from the page within a specified time | App            |
-| text [] property [] is []                   | check that the specified attribute of the specified text in the page is the specified value | ALL            |
-| element [] property [] is []                | check that the specified attribute of the specified element in the page is the specified value | ALL            |
-| element [] position not change in []seconds | check that the position of the specified element on the page has not changed within the specified time | App            |
-| the text of element [] is []                | check that the text of the specified element in the page is equal to the specified value | ALL            |
-| the text of element [] include []           | check that the text of the specified element in the page contains the specified value | ALL            |
-| go to home page                             | go to home page                                              | ALL            |
-| screenshot                                  | save the current screen image                                | ALL            |
-| start record                                | start recording video                                        | App            |
-| start recording timeout []                  | start recording screen and set timeout                       | App            |
-| stop record                                 | stop recording video                                         | ALL            |
-| connect device []                           | connect test device                                          | App            |
-| install app[]                               | install app                                                  | android        |
-| delete app[]                                | delete app                                                   | android        |
-| start app[]                                 | start app                                                    | App            |
-| retart app                                  | retart app                                                   | App            |
-| close app                                   | close app                                                    | App            |
-| logon account[] password []                 | login with account and password                              | ALL            |
-| logout                                      | logout                                                       | ALL            |
-| return to previous page                     | return to previous page                                      | Android 、 web |
-| in[]from [] find [] element                 | Find the element of the specified selector by sliding in the specified direction within the element that refers to the selector | ALL            |
-| from [] select []                           | Select the specified value in the web select element         | web            |
-| exist [] subNode [] element                 | check that  a parent element exists, and a child element exists under that parent element | web            |
-| the text of element [] subNode [] is []     | check that  a parent element exists, and the text of a child element under that parent element is the specified string | web            |
-| -----                                       | -----                                                        | -----          |
+| statement template                                           | description                                                  | suitable for   |
+| ------------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
+| go to url[]                                                  | jump to the specified url address                            | android 、web  |
+| wait [] seconds                                              | Wait for a while                                             | ALL            |
+| element [] appear after page rendering                       | check whether the specified element has been rendered when entering a new page | ALL            |
+| click[]                                                      | click on the element with the specified attribute            | ALL            |
+| click text[]                                                 | Click on the element of the specified text                   | ALL            |
+| click position[][]                                           | click to specify the location on the screen                  | ALL            |
+| input [] into []                                             | input a string in the specified selector                     | ALL            |
+| clear [] and input[]                                         | clear and input a string in the specified selector           | Web            |
+| from [] find [] element                                      | find the element with the specified attribute in the specified direction | ALL            |
+| slide to [] distance []                                      | swipe the specified distance in the specified direction on the full screen | ALL            |
+| [] slide to [] distance []                                   | slide the specified distance in the specified direction in the specified area | ALL            |
+| exist text []                                                | check that the specified string exists in the page           | ALL            |
+| not exist text []                                            | check that the specified string does not exist in the page   | ALL            |
+| exist element []                                             | check the presence of elements with specified attributes on the page | ALL            |
+| not exist element []                                         | check that there is no element with the specified attribute in the page | ALL            |
+| element [] disappear                                         | check that the element with the specified attribute in the page disappears within the specified time | App            |
+| text[]disappear                                              | check that the specified string in the page disappears from the page within a specified time | App            |
+| text [] property [] is []                                    | check that the specified attribute of the specified text in the page is the specified value | ALL            |
+| element [] property [] is []                                 | check that the specified attribute of the specified element in the page is the specified value | ALL            |
+| element [] position not change in []seconds                  | check that the position of the specified element on the page has not changed within the specified time | App            |
+| the text of element [] is []                                 | check that the text of the specified element in the page is equal to the specified value | ALL            |
+| the text of element [] include []                            | check that the text of the specified element in the page contains the specified value | ALL            |
+| go to home page                                              | go to home page                                              | ALL            |
+| screenshot                                                   | save the current screen image                                | ALL            |
+| start record                                                 | start recording video                                        | App            |
+| start recording timeout []                                   | start recording screen and set timeout                       | App            |
+| stop record                                                  | stop recording video                                         | ALL            |
+| connect device []                                            | connect test device                                          | App            |
+| install app[]                                                | install app                                                  | android        |
+| delete app[]                                                 | delete app                                                   | android        |
+| start app[]                                                  | start app                                                    | App            |
+| retart app                                                   | retart app                                                   | App            |
+| close app                                                    | close app                                                    | App            |
+| logon account[] password []                                  | login with account and password                              | ALL            |
+| logout                                                       | logout                                                       | ALL            |
+| return to previous page                                      | return to previous page                                      | Android 、 web |
+| in[]from [] find [] element                                  | Find the element of the specified selector by sliding in the specified direction within the element that refers to the selector | ALL            |
+| from [] select []                                            | Select the specified value in the web select element         | web            |
+| exist [] subNode [] element                                  | check that  a parent element exists, and a child element exists under that parent element | web            |
+| the text of element [] subNode [] is []                      | check that  a parent element exists, and the text of a child element under that parent element is the specified string | web            |
+|                                                              |                                                              |                |
+| cache service request [operation[,operation ...]]            | Cache the last request body  for this service locally<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| remove service request cache[operation[,operation ...]]      | Clear cached request body for this operation from local<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| remove all service request caches                            | Remove all operations cached request body                    | web            |
+| listening service [operation[,operation ...]] bind mockCase[mockCaseId[,mockCaseId ...]] | Monitor the operation request and intercept it, and mock data with the return message of mockCaseId<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| remove service listener [operation[,operation ...]]          | Remove request listener for this operation<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| remove all service listeners                                 | remove all request listeners                                 | web            |
+| compare service request operation] with json file [target_data_path] | Compare the cached  request body of the operation with the file content corresponding to target_data_path<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| compare service non-json request [operation] with non-json file [target_data_path] | Compare the non-json cached request body of the operation with the file content corresponding to target_data_path<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| service request [operation] request parameter [target_json_path] is [expect_value] | Check whether the value of the corresponding parameter of the request body cached by the operation is consistent with the given expected value<br />Note: `operation` is the string between the last  `\` and  `?` in the url | web            |
+| -----                                                        | -----                                                        | -----          |
 
 
 
@@ -302,3 +312,167 @@ from left find [testId, distance=0.5, duration=2, swipeCount=8] element
 ```js
 go to url [list page]
 ```
+
+**cache service request [operation[,operation ...]]**
+
+- platform：Web
+- description： Cache the last request body  for this service locally<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```js
+//Example1：
+cache service request [getRecommendHotelList]
+
+//Example2：
+cache service request [getRecommendHotelList,writecookie]
+```
+
+**remove service request cache[operation[,operation ...]]**
+
+- platform：Web
+- description：Clear cached request body for this operation from local<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```js
+//Example1：
+remove service request cache [getRecommendHotelList]
+
+//Example2：
+remove service request cache [getRecommendHotelList,writecookie]
+```
+
+**remove all service request caches**
+
+- platform：Web
+- description：Remove all operations cached request body
+
+**listening service [operation[,operation ...]] bind mockCase[mockCaseId[,mockCaseId ...]]**
+
+- platform：Web
+- description：Monitor the operation request and intercept it, and mock data with the return message of mockCaseId<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+- **MockCase Configuration**：
+
+  ​	The `mock` data of the service listening `step` statement can be obtained in two ways: **json file configuration** and **function call**.
+
+  -  **json file configuration**：Example 1 below. For the specific setting method and format, please refer to the json file in the **Demo** project **mockCaseData** directory.
+
+    In this method, it should be noted that the `mockCaseId` (json key, such as `4245512` in example 1) corresponding to `response` needs to be unique in the entire `mockCaseData` directory, otherwise the mock data will be used by others with the same `key` data is overwritten.
+
+  - **function call**:  Custom handling with getting `MockData`. This approach requires the implementation of the `get_mock_case_body(mock_case_id)` extension method in the **pscript/custom_handle/operation.py** file of the **Demo** project.
+
+  The `MockCase` binding data prefers the result returned by the custom extension method. When the result of the custom extension method is None, the framework will try to find all json files in the project **mockCaseData** directory and return the mock data corresponding to `mock_case_id` in the json file.
+
+**Mock data configuration example I: json file configuration**
+
+```json
+{
+  "4245512": {
+    "count": 101,
+    "results": [
+      {
+        "id": 10,
+        "name": "test-狮子王",
+        "alias": "The Lion King",
+        "cover": "https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c",
+        "categories": [
+          "动画",
+          "歌舞",
+          "冒险"
+        ],
+        "published_at": "1995-07-15",
+        "minute": 89,
+        "score": 9.0,
+        "regions": [
+          "美国"
+        ]
+      }
+    ]
+  }
+}
+```
+
+**Syntax usage example:**
+
+```js
+//Example1：
+listening service [movie] bind mockCase[4245512]
+
+//Example2：
+listening service [movie,testList] bind mockCase[4245512,123456]
+```
+
+**remove service listener [operation[,operation ...]]**
+
+- platform：Web
+- description：Remove request listener for this operation<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```js
+//Example1：
+remove service listener [movie]
+
+//Example2：
+remove service listener [movie,testList]
+```
+
+**remove all service listeners**
+
+- platform：Web
+- description：remove all request listeners
+
+**compare service request operation] with json file [target_data_path]**
+
+- 支持平台：Web
+- description：Compare the cached  request body of the operation with the file content corresponding to target_data_path<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```js
+compare service request [getRecommendHotelList] with json file [compareData/getRecommendHotelList.json]
+```
+
+**compare service non-json request [operation] with non-json file [target_data_path]**
+
+- platform：Web
+- description：Compare the non-json cached request body of the operation with the file content corresponding to target_data_path<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```gherkin
+compare service non-json request [writecookie] with non-json file [compareData/writecookie.txt]
+```
+
+**service request [operation] request parameter [target_json_path] is [expect_value]**
+
+- platform：Web
+- description：Check whether the value of the corresponding parameter of the request body cached by the operation is consistent with the given expected value<br />Note: `operation` is the string between the last  `\` and  `?` in the url
+
+```gherkin
+# Examples
+service request [getRecommendHotelList] request parameter [head.syscode] is [PC]
+service request [getRecommendHotelList] request parameter [$.cityId] is [2]
+service request [getRecommendHotelList] request parameter [cityId] is [2]
+```
+
+- 🍖**Configure ignore nodes：**
+
+​		Service request comparison supports setting **ignore node**, including **specific path** and **regular expression**. For the specific setting method and format, please refer to the json file in the **Demo** project **interfaceIgnoreConfig** directory.
+
+**Example：**
+
+```json
+{
+  "getRecommendHotelList": [
+    "head.cid",
+    "regex: root\\['head'\\]\\['extension'\\]\\[\\d+\\]\\['value'\\]"
+  ],
+  "writecookie": [
+    "token"
+  ]
+}
+```
+
+**Description**：
+
+- `getRecommendHotelList`, `writecookie` are the request name `operation` of the service request
+
+- `head.cid` is the specific node path in the request body of the service `getRecommendHotelList`. This node will be ignored when comparing messages with files.
+
+- `regex: root\\['head'\\]\\['extension'\\]\\[\\d+\\]\\['value'\\]` is a regular expression. All nodes matching this path in the `getRecommendHotelList` request body will be ignored when comparing.
+
+    	🧨**Note:** When configuring a regular expression, please use `regex:` before the string to declare it.
+
diff --git a/docs/casedsl_zhCN.md b/docs/casedsl_zhCN.md
index 33a78d55..93204668 100644
--- a/docs/casedsl_zhCN.md
+++ b/docs/casedsl_zhCN.md
@@ -7,49 +7,58 @@
 
 ## 以下是常用语句模板：
 
-| 语句模板                         | 语义                                                       | 适用于         |
-| -------------------------------- | ---------------------------------------------------------- | -------------- |
-| 跳转到[]                         | 跳转到指定的url地址                                        | android 、web  |
-| 等待[]秒                         | 等待一段时间                                               | ALL            |
-| 页面渲染完成出现元素[]           | 进入新的页面时检查指定元素是否渲染完成                     | ALL            |
-| 点击[]                           | 点击指定属性的元素                                         | ALL            |
-| 点击文案[]                       | 点击指定文案的元素                                         | ALL            |
-| 点击屏幕位置[][]                 | 点击屏幕指定位置                                           | ALL            |
-| 在 [] 中输入[]                   | 在指定选择器中输入字符串                                   | ALL            |
-| 在[]中清空并输入[]               | 在指定选择器中清空并输入字符串                             | Web            |
-| 向[] 查找[]的元素                | 向指定方向查找指定属性的元素                               | ALL            |
-| 全屏向[] 滑动[]                  | 全屏向指定方向滑动指定距离                                 | ALL            |
-| [] 向[] 滑动[]                   | 在指定区域内向指定方向滑动指定距离                         | ALL            |
-| 存在[]的文案                     | 检查页面中存在指定的字符串                                 | ALL            |
-| 不存在[]的文案                   | 检查页面中不存在指定的字符串                               | ALL            |
-| 存在[]的元素                     | 检查页面中存在指定属性的元素                               | ALL            |
-| 不存在[]的元素                   | 检查页面中不存在指定属性的元素                             | ALL            |
-| 元素[]消失                       | 检查页面中指定属性的元素在指定时间内消失                   | App            |
-| 文案[]消失                       | 检查页面中指定的字符串在规定时间内从页面消失               | App            |
-| 文案[] 的属性[] 为 []            | 检查页面中指定文案的指定属性为指定值                       | ALL            |
-| 元素[] 的属性[] 为 []            | 检查页面中指定元素的指定属性为指定值                       | ALL            |
-| 元素[] 位置[] 秒内未变动         | 检查页面中指定元素的位置在指定时间内未发生变化             | App            |
-| [] 的文案为[]                    | 检查页面中指定元素的文案等于指定值                         | ALL            |
-| [] 的文案包含[]                  | 检查页面中指定元素的文案包含指定值                         | ALL            |
-| 回到首页                         | 回到首页                                                   | ALL            |
-| 全屏截图                         | 保存当前屏幕图像                                           | ALL            |
-| 开始录屏                         | 开始录制视频                                               | App            |
-| 开始录屏超时[]                   | 开始录屏并设置超时时间                                     | App            |
-| 结束录屏                         | 结束录制视频                                               | ALL            |
-| 连接设备[]                       | 连接测试设备                                               | App            |
-| 安装APP[]                        | 安装APP                                                    | android        |
-| 删除APP[]                        | 删除APP                                                    | android        |
-| 启动APP[]                        | 启动APP                                                    | App            |
-| 重启APP                          | 重启APP                                                    | App            |
-| 关闭App                          | 关闭App                                                    | App            |
-| 登录账号[] 密码[]                | 使用账号密码进行登录                                       | ALL            |
-| 退出登录                         | 退出系统登录                                               | ALL            |
-| 返回上一页                       | 返回上一页面                                               | Android 、 web |
-| 在[]中向[]查找[]的元素           | 在指定 选择器 的元素内 向指定方向滑动查找 指定选择器的元素 | ALL            |
-| 在[]中选择[]                     | 在web页面下拉框元素中选择指定值                                    | web            |
-| 存在[父选择器]的[子选择器]的元素 | 存在某个父元素，并且该父元素下存在某个子元素               | web            |
-| [父选择器]的[子选择器]的文案为[] | 存在某个父元素，并且该父元素下某个子元素的文案为指定字符串 | web            |
-| \-----                           | \-----                                                     | \-----         |
+| 语句模板                                                     | 语义                                                         | 适用于         |
+| ------------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
+| 跳转到[]                                                     | 跳转到指定的url地址                                          | android 、web  |
+| 等待[]秒                                                     | 等待一段时间                                                 | ALL            |
+| 页面渲染完成出现元素[]                                       | 进入新的页面时检查指定元素是否渲染完成                       | ALL            |
+| 点击[]                                                       | 点击指定属性的元素                                           | ALL            |
+| 点击文案[]                                                   | 点击指定文案的元素                                           | ALL            |
+| 点击屏幕位置[][]                                             | 点击屏幕指定位置                                             | ALL            |
+| 在 [] 中输入[]                                               | 在指定选择器中输入字符串                                     | ALL            |
+| 在[]中清空并输入[]                                           | 在指定选择器中清空并输入字符串                               | Web            |
+| 向[] 查找[]的元素                                            | 向指定方向查找指定属性的元素                                 | ALL            |
+| 全屏向[] 滑动[]                                              | 全屏向指定方向滑动指定距离                                   | ALL            |
+| [] 向[] 滑动[]                                               | 在指定区域内向指定方向滑动指定距离                           | ALL            |
+| 存在[]的文案                                                 | 检查页面中存在指定的字符串                                   | ALL            |
+| 不存在[]的文案                                               | 检查页面中不存在指定的字符串                                 | ALL            |
+| 存在[]的元素                                                 | 检查页面中存在指定属性的元素                                 | ALL            |
+| 不存在[]的元素                                               | 检查页面中不存在指定属性的元素                               | ALL            |
+| 元素[]消失                                                   | 检查页面中指定属性的元素在指定时间内消失                     | App            |
+| 文案[]消失                                                   | 检查页面中指定的字符串在规定时间内从页面消失                 | App            |
+| 文案[] 的属性[] 为 []                                        | 检查页面中指定文案的指定属性为指定值                         | ALL            |
+| 元素[] 的属性[] 为 []                                        | 检查页面中指定元素的指定属性为指定值                         | ALL            |
+| 元素[] 位置[] 秒内未变动                                     | 检查页面中指定元素的位置在指定时间内未发生变化               | App            |
+| [] 的文案为[]                                                | 检查页面中指定元素的文案等于指定值                           | ALL            |
+| [] 的文案包含[]                                              | 检查页面中指定元素的文案包含指定值                           | ALL            |
+| 回到首页                                                     | 回到首页                                                     | ALL            |
+| 全屏截图                                                     | 保存当前屏幕图像                                             | ALL            |
+| 开始录屏                                                     | 开始录制视频                                                 | App            |
+| 开始录屏超时[]                                               | 开始录屏并设置超时时间                                       | App            |
+| 结束录屏                                                     | 结束录制视频                                                 | ALL            |
+| 连接设备[]                                                   | 连接测试设备                                                 | App            |
+| 安装APP[]                                                    | 安装APP                                                      | android        |
+| 删除APP[]                                                    | 删除APP                                                      | android        |
+| 启动APP[]                                                    | 启动APP                                                      | App            |
+| 重启APP                                                      | 重启APP                                                      | App            |
+| 关闭App                                                      | 关闭App                                                      | App            |
+| 登录账号[] 密码[]                                            | 使用账号密码进行登录                                         | ALL            |
+| 退出登录                                                     | 退出系统登录                                                 | ALL            |
+| 返回上一页                                                   | 返回上一页面                                                 | Android 、 web |
+| 在[]中向[]查找[]的元素                                       | 在指定 选择器 的元素内 向指定方向滑动查找 指定选择器的元素   | ALL            |
+| 在[]中选择[]                                                 | 在web页面下拉框元素中选择指定值                              | web            |
+| 存在[父选择器]的[子选择器]的元素                             | 存在某个父元素，并且该父元素下存在某个子元素                 | web            |
+| [父选择器]的[子选择器]的文案为[]                             | 存在某个父元素，并且该父元素下某个子元素的文案为指定字符串   | web            |
+| 缓存服务请求[operation[,operation ...]]                      | 缓存该服务的最后一次请求报文到本地。 <br />注意： operation 是 url 最后一个\ 和 ？ 中间的字符串，即请求名 | web            |
+| 移除请求缓存[operation[,operation ...]]                      | 从本地清除该服务的请求缓存报文。<br />注意： operation 是 url 最后一个\ 和 ？ 中间的字符串，即请求名 | web            |
+| 移除所有请求缓存                                             | 移除所有请求的缓存报文                                       | web            |
+| 监听服务[operation[,operation ...]]绑定MockCase[mockCaseId[,mockCaseId ...]] | 监听相关operation的请求并拦截，用mockCaseId的返回报文进行替换<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串 | web            |
+| 移除服务监听[operation[,operation ...]]                      | 移除operation 的请求监听<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串 | web            |
+| 移除所有服务监听                                             | 移除所有请求监听                                             | web            |
+| 验证服务请求[operation]与[target_data_path]一致              | 比对相关operation的缓存报文与target_data_path对应的文件内容<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串 | web            |
+| 验证服务非json请求[operation]与[target_data_path]一致        | 比对相关operation的非json类型的缓存报文与target_data_path对应的文件内容<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串 | web            |
+| 验证服务[operation]的请求参数[target_json_path]与[expect_value]一致 | 检查相关operation的缓存报文体对应参数的值与给定的期望值是否一致<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串 | web            |
+| \-----                                                       | \-----                                                       | \-----         |
 
 
 ## 语句模版：
@@ -321,3 +330,164 @@ timeout 等待消失的超时时间 ，优先级：默认值 < flybirds_config.j
 ```js
 跳转到[首页]
 ```
+
+**缓存服务请求[operation[,operation ...]]**
+
+- 支持平台：Web
+- 语义：缓存该服务的最后一次请求报文到本地。 <br />注意： operation 是 url 最后一个\ 和 ？ 中间的字符串，即请求名
+
+```js
+//示例1：
+缓存服务请求[getRecommendHotelList]
+
+//示例2：
+缓存服务请求[getRecommendHotelList,writecookie]
+```
+
+**移除请求缓存[operation[,operation ...]]**
+
+- 支持平台：Web
+- 语义：从本地清除该服务请求的缓存报文。<br />注意： operation 是 url 最后一个\ 和 ？ 中间的字符串，即请求名
+
+```js
+//示例1：
+移除请求缓存[getRecommendHotelList]
+
+//示例2：
+移除请求缓存[getRecommendHotelList,writecookie]
+```
+
+**移除所有请求缓存**
+
+- 支持平台：Web
+- 语义：移除所有请求的缓存报文
+
+**监听服务[operation[,operation ...]]绑定MockCase[mockCaseId[,mockCaseId ...]]**
+
+- 支持平台：Web
+- 语义：监听相关operation的请求并拦截，用mockCaseId的返回报文进行替换。<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串
+
+- **MockCase配置**：
+
+  ​	服务监听`step`语句的`mock`数据支持通过2种方式来获取：**json文件配置** 和 **函数调用**。
+
+  - **json文件配置**：如以下示例一。具体设置方式及格式可以参考**Demo**项目**mockCaseData** 目录下的json文件。
+
+    此方式需要注意，对应`response` 的`mockCaseId` （json key，如示例一中的`4245512`）在整个`mockCaseData`目录下需要是唯一的，否则该mock数据会被其他具有相同 `key` 的数据覆盖掉。
+
+  - **函数调用**：自定义处理与获取`MockData`。此种方式需要在 **Demo**项目的**pscript/custom_handle/operation.py** 文件中实现 `get_mock_case_body(mock_case_id)` 扩展方法。
+
+  `MockCase` 绑定的报文优先以自定义扩展方法的返回结果为主。当自定义扩展方法返回结果为None时，框架会尝试查找项目**mockCaseData** 目录下的所有json文件，并返回json文件中`mock_case_id` 对应的 mock数据。
+
+**Mock数据配置示例一：json文件配置**
+
+```json
+{
+  "4245512": {
+    "count": 101,
+    "results": [
+      {
+        "id": 10,
+        "name": "test-狮子王",
+        "alias": "The Lion King",
+        "cover": "https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c",
+        "categories": [
+          "动画",
+          "歌舞",
+          "冒险"
+        ],
+        "published_at": "1995-07-15",
+        "minute": 89,
+        "score": 9.0,
+        "regions": [
+          "美国"
+        ]
+      }
+    ]
+  }
+}
+```
+
+**语法使用示例:**
+
+```js
+//示例1：
+监听服务[movie]绑定MockCase[4245512]
+
+//示例2：
+监听服务[movie,testList]绑定MockCase[4245512,123456]
+```
+
+**移除服务监听[operation[,operation ...]]**
+
+- 支持平台：Web
+- 语义：移除operation 的请求监听<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串
+
+```js
+//示例1：
+移除服务监听[movie]
+
+//示例2：
+移除服务监听[movie,testList]
+```
+
+**移除所有服务监听**
+
+- 支持平台：Web
+- 语义： 移除所有请求监听
+
+**验证服务请求[operation]与[target_data_path]一致**
+
+- 支持平台：Web
+- 语义：比对相关operation的缓存报文与target_data_path对应的文件内容<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串
+
+```js
+验证服务请求[getRecommendHotelList]与[compareData/getRecommendHotelList.json]一致
+```
+
+**验证服务非json请求[operation]与[target_data_path]一致**
+
+- 支持平台：Web
+- 语义：比对相关operation的非json类型的缓存报文与target_data_path对应的文件内容<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串
+
+```js
+验证服务非json请求[writecookie]与[compareData/writecookie.txt]一致
+```
+
+**验证服务[operation]的请求参数[target_json_path]与[expect_value]一致**
+
+- 支持平台：Web
+- 语义：检查相关operation的缓存报文体对应参数的值与给定的期望值是否一致<br />注意：operation 是url 最后一个\ 和 ？ 中间的字符串
+
+```js
+验证服务[getRecommendHotelList]的请求参数[head.syscode]与[PC]一致
+```
+
+- 🍖**配置忽略节点：**
+
+​		服务报文比对支持设置**忽略节点**，包括**具体路径**和**正则表达式**。具体设置方式及格式可以参考**Demo**项目**interfaceIgnoreConfig** 目录下的json文件。
+
+**示例：**
+
+```json
+{
+  "getRecommendHotelList": [
+    "head.cid",
+    "regex: root\\['head'\\]\\['extension'\\]\\[\\d+\\]\\['value'\\]"
+  ],
+  "writecookie": [
+    "token"
+  ]
+}
+```
+
+**说明**：
+
+- `getRecommendHotelList`、`writecookie` 为 服务请求的请求名`operation` 
+
+- `head.cid` 为服务`getRecommendHotelList` 请求体的具体节点路径。与文件进行报文比对时，该节点会被忽略。
+
+- `regex: root\\['head'\\]\\['extension'\\]\\[\\d+\\]\\['value'\\]`  是一个正则表达式。 `getRecommendHotelList` 请求体中所有匹配该路径的节点在比对时都将被忽略。
+
+     🧨  **注意：** 配置正则表达式时，请在字符串前用`regex:`进行标注申明。
+
diff --git a/docs/demoproject.md b/docs/demoproject.md
index f2416ac2..68510a22 100644
--- a/docs/demoproject.md
+++ b/docs/demoproject.md
@@ -55,38 +55,50 @@ When testing on **mobile**, you must configure the items: `deviceId`、`packageN
 
   Supported browser types: `chromium`, `firefox` and `webkit`. Required for `platform=web`. Multiple values are supported to be configured at the same time. For example: `browserType`: ["firefox", "chromium", "webkit"].  Default is: "browserType": ["chromium"].
 
+- `requestInterception` 
+
+  Enable request blocking。Required for `platform=web`。Default is: `true`.
+
+- `ignoreOrder` 
+
+​		List difference ignoring order or duplicates.  Default is：`false`。Valid only when  `requestInterception=true`.
+
+- `abortDomainList` 
+
+​		List of domains that abort routes when requests are blocked. For example："abortDomainList": ["google.com"]. Valid only when  `requestInterception=true`.
+
 - `beforeRunPage` 
 
   Configure the behavior of the app before starting the test. By default, "restart the app" to ensure that the page is on the main homepage during the test, and startApp (start the app), stopApp (close the app), and None (no operation), default: "restartApp"
 
 - `scenarioFailPage` 
-  
+
   The configuration behavior of the app after the execution of the test case fails. the default is to "restart the app" to ensure that there will not be too many historical pages in the current memory, as well as backupPage (return to the previous page), stopApp (stop the app), None (nothing) operate), default: "restartApp"
 
 - `scenarioSuccessPage`
-  
+
   The configuration behavior of the app after the test case is executed successfully, the default is "None" without any operation, there are restartApp (restart app), backupPage (return to the previous page), stopApp (stop app), default: "None"
-  
+
 - `beforeRunLogin` 
 
   if need to log in before starting the test, default: "false"
-  
+
 - `failScreenRecord`  
-  
+
   associate the execution screen recording file of the use case to the test report after the failure, default: "true"
-  
+
 - `scenarioScreenRecordTime`
 
   the maximum duration of the recording screen, default: 120
-  
+
 - `failRerun`
 
   Whether to rerun after failure, default: "true"
-  
+
 - `maxFailRerunCount`
 
   Number of failures satisfied by failed reruns, default: 1
-  
+
 - `maxRetryCount`
 
   Number of failed retries, default: 2
@@ -94,74 +106,74 @@ When testing on **mobile**, you must configure the items: `deviceId`、`packageN
 - `waitEleTimeout`
 
   Timeout for finding elements in the page, default: 15
-  
+
 - `waitEleDisappear`
 
   The timeout period for the disappearance of the specified element on the page, default: 10
-  
+
 - `clickVerifyTimeout`
 
   The timeout for the click operation to judge the completion of the rendering, default: 15
-  
+
 - `useSwipeDuration`
 
   Use the globally configured sliding time, default: "false"
-  
+
 - `swipeDuration`
 
   When useSwipeDuration is true, the time of the sliding operation is this value, default: "false"
-  
+
 - `usePocoInput`
 
   whether to use poco's input method, airtest is used by default, default: "false"
-  
+
 - `afterInputWait`
 
   Waiting time after entering the input box, default: 1
-  
+
 - `useSearchSwipeDuration`
 
   Whether to use the global sliding time in the sliding search, default: "false"
-  
+
 - `searchSwipeDuration`
-  
+
   useSearchSwipeDuration is true, all sliding time in the sliding search is set globally by this value, default: 1
-  
+
 - `swipeSearchCount`
 
   The maximum number of swipes to find an element, default: 5
-  
+
 - `swipeSearchDistance`
 
   The distance of each swipe in the swipe search, default: 0.3
-  
+
 - `pageRenderTimeout` 
 
   The time to wait for the page rendering to complete, the global configuration time of the timeout parameter in the sentence "The page rendering is complete and the element [selector{, path=false, multiSelector=false, timeout=10}]" appears, default: 35
-  
+
 - `appStartTime`
-  
+
   The timeout after APP start, default: 6
-  
+
 - `swipeReadyTime` 
 
   Waiting time before sliding starts, default: 3
-  
+
 - `verifyPosNotChangeCount` 
 
   The maximum number of judgments for judging that the position of the element has not changed, default: 5
-  
+
 - `screenRecordTime`
   screen record maxmun time, default: 60
 
 - `useSnap`
 
    Whether to use snapshots to find text, default: true
-  
+
 - `useAirtestRecord`
 
    Use airtest record, default: "true"
-   
+
    
 
 #### **schema_url.json**
@@ -222,13 +234,26 @@ For unified configuration of how to position elements on multiple platforms.
 
 Store custom scripts in python language (including custom step statements, docking with other platforms such as mocks, custom schema jump logic, login and logout, extension of various hook functions when behave is running, parameter processing methods, etc.)
 
-app/operation.py : Define some app-specific behaviors, such as the splicing of the schema jump protocol, login, logout, and jump to the homepage
-
-- dsl.step：Custom dsl statements, if you create a new .py file to write custom statements, you need to import the .py file in feature/steps/steps.py
-
-- dsl.hook：The extension of each hook function in the execution process, so that users can exert their own influence on the execution process
+- **custom_handle/operation.py** : Define some app、web specific behaviors, such as the splicing of the schema jump protocol, login, logout, and jump to the homepage in app, and  creates a new  BrowserContext, get Mock data in web.
+
+  Example : Web-side custom creates BrowserContext
+
+  ```python
+  def create_browser_context(browser):
+      """
+      custom creates a new browser context.
+      :param browser: the browser instance
+      """
+      # For example, adding a 'local' parameter when create.
+      context = browser.new_context(record_video_dir="videos",
+                                    ignore_https_errors=True,
+                                     locale="en")
+      return context
+  ```
 
-- other_platform：Stores some code that interacts with other platforms, which can be added or deleted
+  
 
-- params_deal：Store some custom processing methods
+- **dsl.step**：Custom dsl statements, if you create a new .py file to write custom statements, you need to import the .py file in feature/steps/steps.py
+- **dsl.hook**：The extension of each hook function in the execution process, so that users can exert their own influence on the execution process
+- **params_deal**：Store some custom processing methods
 
diff --git a/docs/demoproject_zhCN.md b/docs/demoproject_zhCN.md
index 4cfe0a71..89683e34 100644
--- a/docs/demoproject_zhCN.md
+++ b/docs/demoproject_zhCN.md
@@ -44,14 +44,14 @@
 - `platform` 
 
     项目case执行的平台，目前支持`android` 、`ios` 和 `web`, 不填时默认为：`android`
-  
+
 - `webDriverAgent` 
 
   设备里WebDriverAgent的BundleID，可通过`tidevice applist`命令查看。连接IOS设备时必填。
 
 - `headless` 
 
-	浏览器的运行模式，为 true 时表示浏览器将以**无头**方式运行。`platform=web`时必填。默认为：`true`
+  浏览器的运行模式，为 true 时表示浏览器将以**无头**方式运行。`platform=web`时必填。默认为：`true`
 
 - `browserType` 
 
@@ -59,6 +59,18 @@
 
   如："browserType": ["firefox","chromium","webkit"]。 默认为："browserType": ["chromium"]。
 
+- `requestInterception` 
+
+    开启请求拦截。`platform=web`时必填。默认为：`true`
+
+- `ignoreOrder` 
+
+​		请求报文比对时忽略报文节点的顺序或重复的列表差异。默认为：`false`。仅在`requestInterception=true`时有效。
+
+- `abortDomainList` 
+
+​		请求拦截时，终止路由的域名列表。如："abortDomainList": ["google.com"]。仅在`requestInterception=true`时有效。
+
 - `beforeRunPage` 
 
   在开始测试前对app的行为配置，默认时“重启app”保证测试时页面处于大首页，还有startApp(启动app)，stopApp(关闭app)、None(无任何操作), 默认："restartApp"
@@ -74,23 +86,23 @@
 - `beforeRunLogin` 
 
   开始测试前是否需要登陆, 默认："false"
-  
+
 - `failScreenRecord` 
 
   失败后是否需要关联用例的执行录屏文件到测试报告中, 默认："true"
-  
+
 - `scenarioScreenRecordTime` 
 
   failScreenRecord为ture开启失败录屏时，录制屏幕的最大时长, 默认：120
-  
+
 - `failRerun` 
 
   失败后是否重新运行, 默认：true
-  
+
 - `maxFailRerunCount` 
 
   失败重新运行所满足的失败个数, 默认：1
-  
+
 - `maxRetryCount` 
 
   失败重试次数, 默认：2
@@ -98,35 +110,35 @@
 - `waitEleTimeout` 
 
   页面中查找元素的超时时间, 默认：15
-  
+
 - `waitEleDisappear` 
 
   页面中指定元素消失的超时时间, 默认：10
-  
+
 - `clickVerifyTimeout` 
 
   点击操作的判断渲染完成的超时时间, 默认：15
-  
+
 - `useSwipeDuration` 
 
   使用全局配置的滑动时间, 默认："false"
-  
+
 - `swipeDuration` 
 
   当useSwipeDuration为ture时，滑动操作的时间为该值, 默认：6
-  
+
 - `usePocoInput` 
 
   输入是否使用poco的输入方法，默认使用airtest, 默认：false
-  
+
 - `afterInputWait` 
 
   输入框输入后的等待时间, 默认：1
-  
+
 - `useSearchSwipeDuration`
 
   滑动查找中是否使用全局的滑动时间, 默认：false
-  
+
 - `searchSwipeDuration`
 
   useSearchSwipeDuration为ture时，滑动查找中的所有滑动时间由该值全局设置, 默认：1
@@ -138,31 +150,31 @@
 - `swipeSearchDistance` 
 
   滑动查找中每次滑动的距离, 默认：0.3
-  
+
 - `pageRenderTimeout` 
 
   等待页面渲染完成的时间，语句 “页面渲染完成出现元素[选择器{, path=false, multiSelector=false, timeout=10}]” 中的timeout参数的全局配置时间, 默认：35
-  
+
 - `appStartTime` 
 
   APP 启动后等待时间, 默认：6
-  
+
 - `swipeReadyTime` 
 
   滑动开始前的等待时间, 默认：3
-  
+
 - `verifyPosNotChangeCount`
 
   判断元素位置未发生改变的最大判断次数, 默认：5
-  
+
 - `screenRecordTime` 
 
   录屏时间, 默认：60
-  
+
 - `useSnap`
 
   是否使用快照查找文案, 默认：true
-  
+
 - `useAirtestRecord` 
 
   使用airtest录屏, 默认：true
@@ -226,13 +238,26 @@
 ## pscripts目录
 
 存放python语言自定义的脚本（包括自定义step语句，对接如mock等其他平台，自定义schema跳转逻辑，登陆登出，behave运行时各种钩子函数的扩展，参数处理方法等等）
-app/operation.py 定义一些app特有的行为，比如schema跳转协议的拼接，登陆，登出，跳转到首页
-
-- dsl.step：自定义dsl语句，如果新建.py文件来编写自定义语句，需要在feature/steps/steps.py中import 该 .py文件
 
-- dsl.hook：执行过程中各个钩子函数的扩展
-
-- other_platform：存放的是一些与其他平台交互的代码
+- **custom_handle/operation.py** ：定义一些app、web特有的行为。比如，app的 schema跳转协议的拼接，登陆，登出，跳转到首页； web的创建自定义BrowserContext，获取MockData等。
+
+  示例一：web端自定义BrowserContext
+
+  ```python
+  def create_browser_context(browser):
+      """
+      custom creates a new browser context.
+      :param browser: the browser instance
+      """
+      # For example, adding a 'local' parameter when create.
+      context = browser.new_context(record_video_dir="videos",
+                                    ignore_https_errors=True,
+                                     locale="en")
+      return context
+  
+  ```
 
-- params_deal：存放自定义的一些处理方法
+- **dsl.step**：自定义dsl语句，如果新建.py文件来编写自定义语句，需要在feature/steps/steps.py中import 该 .py文件
+- **dsl.hook**：执行过程中各个钩子函数的扩展
+- **params_deal**：存放自定义的一些处理方法
 
diff --git a/flybirds/cli/create_project.py b/flybirds/cli/create_project.py
index d8cde718..0a584437 100644
--- a/flybirds/cli/create_project.py
+++ b/flybirds/cli/create_project.py
@@ -279,7 +279,8 @@ def copy_extend_files(pkg_list, demo_pro_path):
 
         feat_path = os.path.join(os.path.normpath(extend_path), 'features')
         config_path = os.path.join(os.path.normpath(extend_path), 'config')
-
+        custom_handle_path = os.path.join(os.path.normpath(extend_path),
+                                          'pscript', "custom_handle")
         # add extend features
         if feat_path is not None and os.path.exists(feat_path):
             feat_files = get_files_from_dir(feat_path)
@@ -306,6 +307,18 @@ def copy_extend_files(pkg_list, demo_pro_path):
                 shutil.rmtree(demo_config_path)
             shutil.copytree(config_path, demo_config_path)
 
+        # add extend custom operation
+        if custom_handle_path is not None and os.path.exists(
+                custom_handle_path):
+            # config target path
+            demo_custom_handle_path = os.path.join(
+                os.path.normpath(demo_pro_path),
+                'pscript', "custom_handle")
+            if os.path.isdir(demo_custom_handle_path):
+                # target_path is existed
+                shutil.rmtree(demo_custom_handle_path)
+            shutil.copytree(custom_handle_path, demo_custom_handle_path)
+
 
 def write_import_steps(pkg_list, demo_pro_path, site_path):
     """
diff --git a/flybirds/core/config_manage.py b/flybirds/core/config_manage.py
index a72a9a1a..4525dfc8 100644
--- a/flybirds/core/config_manage.py
+++ b/flybirds/core/config_manage.py
@@ -8,6 +8,7 @@
 from flybirds.utils import file_helper
 from flybirds.utils import flybirds_log as log
 from flybirds.utils.dsl_helper import return_value, str2bool
+from flybirds.utils.file_helper import read_json_data
 
 
 class ConfigManage:
@@ -52,6 +53,7 @@ def __init__(self, user_data):
             str(self.flow_behave.__dict__),
         )
         self.ele_locator_info = EleLocator()
+        self.ignore_node_info = IgnoreNodeConfig()
 
 
 def get_config(config, name):
@@ -168,30 +170,43 @@ class WebConfig:
 
     def __init__(self, user_data, config):
         web_info = get_config(config, "web_info")
+        # default value
+        headless = True
+        browser_type = 'chromium'
+        request_interception = True
+        ignore_order = False
+        abort_domain_list = []
+
         if user_data is None and web_info is None:
             log.warn('[web_info] configuration of web_info is none.')
-            # default value
-            self.headless = True
-            self.browser_type = 'chromium'
-            self.default_time_out = 30
+            self.headless = headless
+            self.browser_type = browser_type
+            self.request_interception = request_interception
+            self.ignore_order = ignore_order
+            self.abort_domain_list = abort_domain_list
             return
 
-        headless = user_data.get("headless", web_info["headless"])
-        browser_type = user_data.get("browserType", web_info["browserType"])
-        default_time_out = user_data.get("defaultTimeout",
-                                         web_info["defaultTimeout"])
-        if headless is None:
-            headless = True
+        if web_info.get("headless") is not None:
+            headless = web_info.get("headless")
+        if web_info.get("browserType") is not None:
+            browser_type = web_info.get("browserType")
+        if web_info.get("requestInterception") is not None:
+            request_interception = web_info.get("requestInterception")
+        if web_info.get("ignoreOrder") is not None:
+            ignore_order = web_info.get("ignoreOrder")
+        if web_info.get("abortDomainList") is not None:
+            abort_domain_list = web_info.get("abortDomainList")
+
+        headless = user_data.get("headless", headless)
         if isinstance(headless, str):
             headless = str2bool(headless)
         self.headless = headless
-        if browser_type is None:
-            browser_type = 'chromium'
-        self.browser_type = browser_type
-        if default_time_out is None:
-            # default webTimeout is 30s
-            default_time_out = 30
-        self.default_time_out = default_time_out
+        self.browser_type = user_data.get("browserType", browser_type)
+        self.request_interception = user_data.get("requestInterception",
+                                                  request_interception)
+        self.ignore_order = user_data.get("ignoreOrder", ignore_order)
+        self.abort_domain_list = user_data.get("abortDomainList",
+                                               abort_domain_list)
 
 
 class FlowBehave:
@@ -520,3 +535,16 @@ def __init__(self):
         else:
             self.all_ele_locator = file_helper.get_json_from_file(
                 ele_locator_path)
+
+
+class IgnoreNodeConfig:
+    """
+    all interface ignore node config
+    """
+
+    def __init__(self):
+        interface_ignore_dir_path = os.path.join(
+            os.getcwd(), "interfaceIgnoreConfig"
+        )
+        all_ignore_data = read_json_data(interface_ignore_dir_path)
+        self.all_ignore_nodes = all_ignore_data
diff --git a/flybirds/core/dsl/globalization/i18n.py b/flybirds/core/dsl/globalization/i18n.py
index 290f8b98..800aea24 100644
--- a/flybirds/core/dsl/globalization/i18n.py
+++ b/flybirds/core/dsl/globalization/i18n.py
@@ -135,8 +135,27 @@
         "from [{selector}] select [{param2}]": ["在[{selector}]中选择[{param2}]"],
         "exist [{p_selector}] subNode [{c_selector}] element": [
             "存在[{p_selector}]的[{c_selector}]的元素"],
-        "the text of element [{p_selector}] subNode [{c_selector}] is [{param3}]": [
+        "the text of element [{p_selector}] subNode [{c_selector}] is "
+        "[{param3}]": [
             "[{p_selector}]的[{c_selector}]文案为[{param3}]"
-        ]
+        ],
+        "cache service request [{service}]": ["缓存服务请求[{service}]"],
+        "remove service request cache [{service}]": ["移除请求缓存[{service}]"],
+        "remove all service request caches": ["移除所有请求缓存"],
+        "listening service [{service}] bind mockCase[{mock_case_id}]": [
+            "监听服务[{service}]绑定MockCase[{mock_case_id}]"],
+        "remove service listener [{service}]": ["移除服务监听[{service}]"],
+        "remove all service listeners": ["移除所有服务监听"],
+        "compare service request [{service}] with json file "
+        "[{target_data_path}]": [
+            "验证服务请求[{service}]与[{target_data_path}]一致"],
+        "compare service non-json request [{service}] with non-json file "
+        "[{target_data_path}]": [
+            "验证服务非json请求[{service}]与[{target_data_path}]一致"],
+        "service request [{service}] request parameter [{target_json_path}] "
+        "is [{expect_value}]": [
+            "验证服务[{service}]的请求参数[{target_json_path}]"
+            "与[{expect_value}]一致"]
+
     },
 }
diff --git a/flybirds/core/dsl/step/request.py b/flybirds/core/dsl/step/request.py
new file mode 100644
index 00000000..14ec8b9c
--- /dev/null
+++ b/flybirds/core/dsl/step/request.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+"""
+This module defines the steps related to the web network request.
+"""
+
+from behave import step
+
+from flybirds.core.global_context import GlobalContext as g_Context
+from flybirds.utils.dsl_helper import ele_wrap
+
+
+@step("cache service request [{service}]")
+@ele_wrap
+def add_request_body(context, service=None):
+    """
+    cache information of network request(service)
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    """
+    g_Context.step.add_request_body(context, service)
+
+
+@step("remove service request cache [{service}]")
+@ele_wrap
+def remove_request_body(context, service=None):
+    """
+    remove cached information of network request(service)
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    """
+    g_Context.step.remove_request_body(context, service)
+
+
+@step("remove all service request caches")
+def clear_all_request_body(context):
+    """
+    remove all cached information of network request
+
+    :param context: step context
+    """
+    g_Context.step.clear_all_request_body(context)
+
+
+@step("listening service [{service}] bind mockCase[{mock_case_id}]")
+@ele_wrap
+def add_request_mock(context, service=None, mock_case_id=None):
+    """
+    Listening to network request(service) and binding mock data to it
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    :param mock_case_id: unique key for get mock data.
+         (String, e.g. a numeric string, or other).
+    """
+    g_Context.step.add_request_mock(context, service, mock_case_id)
+
+
+@step("remove service listener [{service}]")
+@ele_wrap
+def remover_request_mock(context, service=None):
+    """
+    remove listening and mock data binding to network request(service)
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    """
+    g_Context.step.remover_request_mock(context, service)
+
+
+@step("remove all service listeners")
+def clear_all_request_mock(context):
+    """
+     remove listening and mock data binding for all services
+    """
+    g_Context.step.clear_all_request_mock(context)
+
+
+@step(
+    "compare service request [{service}] with json file [{target_data_path}]"
+)
+@ele_wrap
+def request_compare(context, service=None, target_data_path=None):
+    """
+    compare and verify the request's post body with the data of the target path
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    :param target_data_path: path of target data to be compared
+    """
+    g_Context.step.request_compare_from_path(context, service,
+                                             target_data_path)
+
+
+@step(
+    "compare service non-json request [{service}] with non-json "
+    "file [{target_data_path}]")
+@ele_wrap
+def request_query_str_compare(context, service=None, target_data_path=None):
+    """
+    compare and verify the post body of a non-json type request with the
+    data in the target path
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    :param target_data_path: path of target data to be compared
+    """
+    g_Context.step.request_query_str_compare_from_path(context, service,
+                                                       target_data_path)
+
+
+@step(
+    "service request [{service}] request parameter [{target_json_path}] "
+    "is [{expect_value}]")
+@ele_wrap
+def request_compare_value(context, service=None, target_json_path=None,
+                          expect_value=None):
+    """
+    compare and verify the request parameter of the service request with
+     the expected value
+
+    :param context: step context
+    :param service: service request name. (string or None).
+    :param target_json_path: json path of request parameter
+    :param expect_value: expected value
+    """
+    g_Context.step.request_compare_value(context, service, target_json_path,
+                                         expect_value)
diff --git a/flybirds/core/exceptions.py b/flybirds/core/exceptions.py
index ad99149f..a48b07db 100644
--- a/flybirds/core/exceptions.py
+++ b/flybirds/core/exceptions.py
@@ -123,3 +123,16 @@ def print_message(param):
         message += f'\nwaiting for selector "{param}"\n'
         message += '=' * 46
         return message
+
+
+class FlybirdsException(Exception):
+    """
+     flybirds exception base class
+    """
+
+    def __init__(self, message):
+        super().__init__()
+        self.message = message
+
+    def __str__(self):
+        return str(self.message)
diff --git a/flybirds/core/global_context.py b/flybirds/core/global_context.py
index d0234d59..3122715c 100644
--- a/flybirds/core/global_context.py
+++ b/flybirds/core/global_context.py
@@ -24,6 +24,7 @@ def __init__(self):
     screen = PluginProxy()
     screen_record = PluginProxy()
     ui_driver = PluginProxy()
+    interception = PluginProxy()
     platform = None
     config = None
     config_processor = []
diff --git a/flybirds/core/global_resource.py b/flybirds/core/global_resource.py
index 05967608..c92ff657 100644
--- a/flybirds/core/global_resource.py
+++ b/flybirds/core/global_resource.py
@@ -30,7 +30,9 @@ def init_glb():
         "web_driver_agent": None,
         "playwright": None,
         "browser": None,
-        "plugin_page": None
+        "plugin_page": None,
+        "interceptionRequest": {},
+        "interceptionValues": {}
     }
 
 
@@ -276,3 +278,19 @@ def get_ele_locator(key):
             f"The [{key}] has no element locator configuration for the"
             f" [{platform}] platform in ele_locator.json")
     return ele_locator.get(platform)
+
+
+def get_service_ignore_nodes(service):
+    """
+    Get the ignore nodes configuration value of the service interface
+    """
+    all_ignore_nodes = _global_dict[
+        "configManage"].ignore_node_info.all_ignore_nodes
+    if all_ignore_nodes is None:
+        log.warn("[get_service_ignore_nodes] cannot find configuration value "
+                 "from interfaceIgnoreConfig folder")
+        return
+    service_ignore_nodes = all_ignore_nodes.get(service)
+    if service_ignore_nodes is None:
+        log.info(f"interface service [{service}] has not set ignore nodes")
+    return service_ignore_nodes
diff --git a/flybirds/core/plugin/event/config.py b/flybirds/core/plugin/event/config.py
index f2865224..46363733 100644
--- a/flybirds/core/plugin/event/config.py
+++ b/flybirds/core/plugin/event/config.py
@@ -30,7 +30,7 @@ def run(context):
         execute load config and set to global context
         """
         try:
-            log.info("start on before hook")
+            log.info("start load configuration file...")
             user_data = gr.get_value("userData")
             log.info(f'[event config] user_data: {user_data}')
             # obtain the failure case information and associate
diff --git a/flybirds/core/plugin/plugins/default/step/app.py b/flybirds/core/plugin/plugins/default/step/app.py
index ffe8fd9e..3d68f71d 100644
--- a/flybirds/core/plugin/plugins/default/step/app.py
+++ b/flybirds/core/plugin/plugins/default/step/app.py
@@ -57,18 +57,18 @@ def return_pre_page(context):
 
 
 def to_app_home(context):
-    schema_goto_module = gr.get_value("projectScript").app_operation
+    schema_goto_module = gr.get_value("projectScript").custom_operation
     to_home = getattr(schema_goto_module, "to_home")
     to_home()
 
 
 def app_login(context, param1, param2):
-    schema_goto_module = gr.get_value("projectScript").app_operation
+    schema_goto_module = gr.get_value("projectScript").custom_operation
     login = getattr(schema_goto_module, "login")
     login(param1, param2)
 
 
 def app_logout(context):
-    schema_goto_module = gr.get_value("projectScript").app_operation
+    schema_goto_module = gr.get_value("projectScript").custom_operation
     logout = getattr(schema_goto_module, "logout")
     logout()
diff --git a/flybirds/core/plugin/plugins/default/step/schema.py b/flybirds/core/plugin/plugins/default/step/schema.py
index 9b60da86..6aa8232f 100644
--- a/flybirds/core/plugin/plugins/default/step/schema.py
+++ b/flybirds/core/plugin/plugins/default/step/schema.py
@@ -13,7 +13,7 @@ def jump_to_page(context, param):
     page_name = param_dict["pageName"]
     schema_url = gr.get_page_schema_url(page_name)
 
-    schema_goto_module = gr.get_value("projectScript").app_operation
+    schema_goto_module = gr.get_value("projectScript").custom_operation
     deal_method = None
     params_deal_module = None
     if "dealMethod" in param_dict.keys():
diff --git a/flybirds/core/plugin/plugins/default/web/element.py b/flybirds/core/plugin/plugins/default/web/element.py
index f30caec9..13f29a23 100644
--- a/flybirds/core/plugin/plugins/default/web/element.py
+++ b/flybirds/core/plugin/plugins/default/web/element.py
@@ -66,33 +66,24 @@ def __init__(self):
         self.page = page_obj.page
 
     def get_ele_locator(self, selector):
-        selector_node, selector_str = self.get_ele_handle(selector)
-        return self.page.locator(selector_str)
-
-    def get_ele_handle(self, selector):
         if selector is None:
             message = f"[get_ele_locator] the param[{selector}] is None."
             raise FlybirdsVerifyEleException(message=message)
-
         param_temp = handle_str(selector)
         param_dict = params_to_dic(param_temp)
         selector_str = param_dict["selector"]
-        selector_node = self.page.query_selector(selector_str)
-        if selector_node is None:
-            if 'text=' not in selector_str:
-                log.warn(
-                    f'[get_ele_handle] has not find element by[{selector_str}'
-                    f'], now try to find by text.')
-                selector_str = 'text=' + selector_str
-                selector_node = self.page.query_selector(selector_str)
-                if selector_node is None:
-                    raise FlybirdsVerifyEleException(selector=selector_str)
-            raise FlybirdsVerifyEleException(selector=selector_str)
-        return selector_node, selector_str
+
+        if "timeout" in param_dict.keys():
+            timeout = param_dict["timeout"]
+        else:
+            timeout = gr.get_frame_config_value("wait_ele_timeout", 30)
+
+        ele_locator = self.page.locator(selector_str)
+        return ele_locator, float(timeout) * 1000
 
     def get_ele_text(self, param):
-        locator = self.get_ele_locator(param)
-        e_text = locator.inner_text()
+        locator, timeout = self.get_ele_locator(param)
+        e_text = locator.inner_text(timeout=timeout)
         if e_text is None or e_text.strip() == '':
             e_text = locator.get_attribute('value')
             if e_text is None or e_text.strip() == '':
@@ -100,14 +91,14 @@ def get_ele_text(self, param):
         return e_text
 
     def ele_click(self, context, param):
-        locator = self.get_ele_locator(param)
-        locator.click()
+        locator, timeout = self.get_ele_locator(param)
+        locator.click(timeout=timeout)
 
     def click_text(self, context, param):
         if 'text=' not in param:
             param = "text=" + param
-        locator = self.get_ele_locator(param)
-        locator.click()
+        locator, timeout = self.get_ele_locator(param)
+        locator.click(timeout=timeout)
 
     def click_coordinates(self, context, x, y):
         self.page.mouse.click(float(x), float(y))
@@ -117,18 +108,26 @@ def ele_text_include(self, context, param_1, param_2):
         verify_helper.text_container(param_2, e_text)
 
     def find_text(self, context, param):
+        param_temp = handle_str(param)
+        param_dict = params_to_dic(param_temp)
+        selector_str = param_dict["selector"]
+
         p_content = self.page.content()
-        if param in p_content:
-            log.info(f'fif_ind_text: [{param}] is success!')
+        if selector_str in p_content:
+            log.info(f'find_text: [{selector_str}] is success!')
         else:
-            message = f"expect to find the text [{param}] in the page, " \
-                      f"but not actually find it"
+            message = f"expect to find the text [{selector_str}] in the " \
+                      f"page, but not actually find it"
             raise FlybirdVerifyException(message)
 
     def find_no_text(self, context, param):
+        param_temp = handle_str(param)
+        param_dict = params_to_dic(param_temp)
+        selector_str = param_dict["selector"]
+
         p_content = self.page.content()
-        if param in p_content:
-            message = f"except text [{param}] not exists in page, " \
+        if selector_str in p_content:
+            message = f"except text [{selector_str}] not exists in page, " \
                       f"but actual has find it."
             raise FlybirdVerifyException(message)
 
@@ -136,11 +135,15 @@ def ele_text_equal(self, context, param_1, param_2):
         e_text = self.get_ele_text(param_1)
         verify_helper.text_equal(param_2, e_text)
 
+    def ele_exist(self, context, param):
+        locator, timeout = self.get_ele_locator(param)
+        locator.element_handle(timeout=timeout)
+
     def ele_not_exist(self, context, param):
         try:
-            self.get_ele_locator(param)
+            self.ele_exist(context, param)
             ele_exists = True
-        except FlybirdsVerifyEleException:
+        except Exception:
             ele_exists = False
 
         if ele_exists:
@@ -149,31 +152,25 @@ def ele_not_exist(self, context, param):
             raise FlybirdVerifyException(message)
 
     def wait_for_ele(self, context, param):
-        if param is None:
-            message = f"[wait_for_ele] the param[{param}] is None."
-            raise FlybirdsVerifyEleException(message=message)
-
-        param_temp = handle_str(param)
-        param_dict = params_to_dic(param_temp)
-        selector_str = param_dict["selector"]
-        self.page.wait_for_selector(selector_str, state='visible')
+        locator, timeout = self.get_ele_locator(param)
+        locator.wait_for(timeout=timeout, state='visible')
 
     def ele_input_text(self, context, param_1, param_2):
-        locator = self.get_ele_locator(param_1)
-        locator.click()
-        locator.fill(param_2)
+        locator, timeout = self.get_ele_locator(param_1)
+        locator.click(timeout=timeout)
+        locator.fill(param_2, timeout=timeout)
         return self.page.wait_for_timeout(100)
 
     def clear_and_input(self, context, param_1, param_2):
-        locator = self.get_ele_locator(param_1)
-        locator.click()
-        locator.fill('')
-        locator.fill(param_2)
+        locator, timeout = self.get_ele_locator(param_1)
+        locator.click(timeout=timeout)
+        locator.fill('', timeout=timeout)
+        locator.fill(param_2, timeout=timeout)
         return self.page.wait_for_timeout(100)
 
     def ele_slide(self, context, param_1, param_2, param_3):
-        locator = self.get_ele_locator(param_1)
-        box = locator.bounding_box()
+        locator, timeout = self.get_ele_locator(param_1)
+        box = locator.bounding_box(timeout=timeout)
         x = box["x"] + box["width"] / 2
         y = box["y"] + box["height"] / 2
         # get scroll direction
@@ -196,27 +193,26 @@ def full_screen_slide(self, context, param_1, param_2):
         self.page.evaluate(f"window.scrollBy({to_x}, {to_y})")
 
     def ele_select(self, context, selector, option_str):
-        locator = None
+        locator, timeout = self.get_ele_locator(selector)
         try:
-            locator = self.get_ele_locator(selector)
             # select by text
-            locator.select_option(label=option_str)
+            locator.select_option(label=option_str, timeout=timeout)
             log.info(f'[ele_select] select option[{option_str}] success.')
         except FlybirdsVerifyEleException as fe:
             raise fe
         except Exception:
             # select by value
             log.warn(f'[ele_select] retry select option[{option_str}].')
-            locator.select_option(option_str)
+            locator.select_option(option_str, timeout=timeout)
 
     def find_full_screen_slide(self, context, param1, param2):
-        locator = self.get_ele_locator(param2)
-        locator.scroll_into_view_if_needed()
+        locator, timeout = self.get_ele_locator(param2)
+        locator.scroll_into_view_if_needed(timeout=timeout)
 
     def get_ele_attr(self, selector, attr_name, params_deal_module=None,
                      deal_method=None):
-        locator = self.get_ele_locator(selector)
-        ele_attr = locator.get_attribute(attr_name)
+        locator, timeout = self.get_ele_locator(selector)
+        ele_attr = locator.get_attribute(attr_name, timeout=timeout)
         if deal_method is not None:
             deal_method = getattr(params_deal_module, deal_method)
             ele_attr = deal_method(ele_attr)
@@ -243,25 +239,28 @@ def is_text_attr_equal(self, context, text_selector, attr_name,
         self.is_ele_attr_equal(context, text_selector, attr_name, target_val)
 
     def is_parent_exist_child(self, context, parent_selector, child_selector):
-        parent_node, p_selector_str = self.get_ele_handle(parent_selector)
+        parent_locator, p_timeout = self.get_ele_locator(parent_selector)
+
         child_temp = handle_str(child_selector)
         child_dict = params_to_dic(child_temp)
         child_str = child_dict["selector"]
-        child_node = parent_node.query_selector(child_str)
-        if child_node is None:
-            if 'text=' not in child_str:
-                child_str = 'text=' + child_str
-                child_node = parent_node.query_selector(child_str)
-                if child_node is None:
-                    raise FlybirdsVerifyEleException(selector=child_str)
-            raise FlybirdsVerifyEleException(selector=child_str)
-        return child_node
+
+        if "timeout" in child_dict.keys():
+            c_timeout = child_dict["timeout"]
+        else:
+            c_timeout = gr.get_frame_config_value("wait_ele_timeout", 30)
+        c_timeout = float(c_timeout) * 1000
+
+        sub_locator = parent_locator.locator(child_str)
+        sub_locator.element_handle(timeout=p_timeout)
+        return sub_locator, c_timeout
 
     def find_text_from_parent(self, context, parent_selector, child_selector,
                               target_text):
-        child_node = self.is_parent_exist_child(context, parent_selector,
-                                                child_selector)
-        e_text = child_node.inner_text()
+        sub_locator, c_timeout = self.is_parent_exist_child(context,
+                                                            parent_selector,
+                                                            child_selector)
+        e_text = sub_locator.inner_text(timeout=c_timeout)
         if e_text is None or e_text.strip() == '':
-            e_text = child_node.get_attribute('value')
+            e_text = sub_locator.get_attribute('value', timeout=c_timeout)
         verify_helper.text_equal(target_text, e_text)
diff --git a/flybirds/core/plugin/plugins/default/web/interception.py b/flybirds/core/plugin/plugins/default/web/interception.py
new file mode 100644
index 00000000..deed2a38
--- /dev/null
+++ b/flybirds/core/plugin/plugins/default/web/interception.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+# @Time : 2022/5/16 17:05
+# @Author : hyx
+# @File : interception.py
+# @desc :web request interception related operations
+import json
+import os
+import re
+from urllib.parse import parse_qs
+
+from deepdiff import DeepDiff
+from jsonpath_ng import parse as parse_path
+
+import flybirds.core.global_resource as gr
+import flybirds.utils.flybirds_log as log
+from flybirds.core.exceptions import FlybirdsException
+
+__open__ = ["Interception"]
+
+from flybirds.utils import file_helper
+from flybirds.utils.file_helper import read_json_data
+
+
+class Interception:
+    """
+    web interception impl
+    """
+    name = "web_interception"
+
+    # -------------------------------------------------------------------------
+    # request interception
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def add_some_interception_request_body(service_str):
+        if service_str is None:
+            log.error(
+                '[addSomeInterceptionRequestBody] param can not be none.')
+            return
+        service_list = service_str.strip().split(',')
+        interception_request = gr.get_value('interceptionRequest')
+
+        for service in service_list:
+            interception_request[service.strip()] = {}
+        gr.set_value('interceptionRequest', interception_request)
+
+    @staticmethod
+    def remove_some_interception_request_body(service_str):
+        service_list = service_str.strip().split(',')
+        interception_request = gr.get_value('interceptionRequest')
+
+        try:
+            for service in service_list:
+                request_body = interception_request.pop(service.strip())
+                log.info(
+                    f'remove data cached by request [{service.strip()}]: '
+                    f'{request_body}')
+        except Exception as e:
+            message = f'[removeSomeInterceptionRequestBody]  ' \
+                      f'has KeyError! error key: {str(e)}'
+            raise FlybirdsException(message)
+        gr.set_value('interceptionRequest', interception_request)
+
+    @staticmethod
+    def clear_interception_request_body():
+        interception_request = gr.get_value('interceptionRequest')
+        interception_request.clear()
+        gr.set_value('interceptionRequest', interception_request)
+
+    # -------------------------------------------------------------------------
+    # request service listening
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def add_some_interception_mock(service_str, mock_case_id_str):
+        if service_str is None or mock_case_id_str is None:
+            log.error('[addSomeInterceptionMock] param can not be none. ')
+            return
+
+        service_list = service_str.strip().split(',')
+        mock_case_id_list = mock_case_id_str.strip().split(',')
+        if len(service_list) != len(mock_case_id_list):
+            message = f"serviceCount[{service_str}] not equal " \
+                      f"mockCaseCount[{mock_case_id_str}]"
+            raise FlybirdsException(message)
+
+        interception_values = gr.get_value('interceptionValues')
+        for i, service in enumerate(service_list):
+            interception_values[service.strip()] = mock_case_id_list[i].strip()
+
+        gr.set_value('interceptionValues', interception_values)
+
+    @staticmethod
+    def remove_some_interception_mock(service_str):
+        service_list = service_str.strip().split(',')
+        interception_values = gr.get_value('interceptionValues')
+
+        try:
+            for service in service_list:
+                case_id = interception_values.pop(service.strip())
+                log.info(f'remove mock data [{case_id}] from request '
+                         f'[{service.strip()}]')
+        except Exception as e:
+            message = f'[removeSomeInterceptionMock]  ' \
+                      f'has KeyError! error key: {str(e)}'
+            raise FlybirdsException(message)
+        gr.set_value('interceptionValues', interception_values)
+
+    @staticmethod
+    def clear_interception_mock():
+        interception_values = gr.get_value('interceptionValues')
+        interception_values.clear()
+        gr.set_value('interceptionValues', interception_values)
+
+    # -------------------------------------------------------------------------
+    # compare service requests
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def request_compare(operation, target_data_path):
+        request_info = get_server_request_body(operation)
+        actual_request_obj = None
+        if request_info is not None and request_info.get('postData'):
+            actual_request_obj = request_info.get('postData')
+        log.info(f'[request_compare] actualObj:{actual_request_obj}')
+        if actual_request_obj is None:
+            message = f'[request_compare] not get listener data for ' \
+                      f'[{operation}]'
+            raise FlybirdsException(message)
+        actual_request_obj = json.loads(actual_request_obj)
+        file_path = os.path.join(os.getcwd(), target_data_path)
+        expect_request_obj = None
+        if os.path.exists(file_path):
+            expect_request_obj = file_helper.get_json_from_file_path(file_path)
+        log.info(f'[request_compare] expectObj:{expect_request_obj}')
+        if expect_request_obj is None:
+            message = f'[request_compare] cannot get data form path' \
+                      f'[{target_data_path}]]'
+            raise FlybirdsException(message)
+        handle_diff(actual_request_obj, expect_request_obj, operation,
+                    target_data_path)
+
+    @staticmethod
+    def request_query_string_compare(operation, target_data_path):
+        request_info = get_server_request_body(operation)
+        actual_request_obj = None
+        if request_info is not None and request_info.get('postData'):
+            actual_request_obj = request_info.get('postData')
+        if actual_request_obj is None:
+            message = f'[requestQuerystringCompare] not get listener data ' \
+                      f'for [{operation}]'
+            raise FlybirdsException(message)
+        actual_request_obj = parse_qs(actual_request_obj)
+
+        file_path = os.path.join(os.getcwd(), target_data_path)
+        expect_request_obj = None
+        if os.path.exists(file_path):
+            expect_request_obj = file_helper.read_file_from_path(file_path)
+        if expect_request_obj is None:
+            message = f'[requestQuerystringCompare] cannot get data form ' \
+                      f'path [{target_data_path}]'
+            raise FlybirdsException(message)
+        expect_request_obj = parse_qs(expect_request_obj)
+        handle_diff(actual_request_obj, expect_request_obj, operation,
+                    target_data_path)
+
+    @staticmethod
+    def request_compare_value(operation, target_json_path, expect_value):
+        request_info = get_server_request_body(operation)
+        json_data = None
+        if request_info and request_info.get('postData'):
+            json_data = request_info.get('postData')
+        if json_data is None:
+            message = f'[requestCompareValue] not get listener data for ' \
+                      f'[{operation}]'
+            raise FlybirdsException(message)
+        json_data = json.loads(json_data)
+        json_path_expr = parse_path(target_json_path)
+        target_values = [match.value for match in
+                         json_path_expr.find(json_data)]
+        log.info(f'[requestCompareValue] get jsonPathData: {target_values}')
+        if len(target_values) == 0:
+            message = f'[requestCompareValue] cannot get the value from ' \
+                      f'path [{target_json_path}] of [{operation}]'
+            raise FlybirdsException(message)
+        if str(target_values[0]) != expect_value:
+            message = f'value not equal, service [{operation}] request ' \
+                      f'parameter [{target_json_path}] actual value:' \
+                      f'[{target_values[0]}], but expect value:' \
+                      f'[{expect_value}]'
+            raise FlybirdsException(message)
+
+
+def get_server_request_body(service):
+    interception_request = gr.get_value('interceptionRequest')
+    if interception_request:
+        return interception_request.get(service)
+    return None
+
+
+def handle_ignore_node(service):
+    exclude_paths = []
+    exclude_regex_paths = []
+    service_ignore_nodes = gr.get_service_ignore_nodes(service)
+    if service_ignore_nodes is None:
+        return exclude_paths, exclude_regex_paths
+    for item in service_ignore_nodes:
+        if 'regex' in item:
+            regex_item = item.split('regex:')[-1].strip()
+            exclude_regex_paths.append(regex_item)
+        else:
+            path = 'root'
+            for level_item in item.split('.'):
+                # identifies whether the item is an array
+                level_item = level_item.strip()
+                item_is_array = re.search(r"([^\[\]]+)\[(\d+)\]",
+                                          level_item) is not None
+                item_str = ''
+                if item_is_array:
+                    property_name = "['" + re.search(r"([^\[\]]+)\[(\d+)\]",
+                                                     level_item).group(
+                        1) + "']"
+                    array_index = ''.join(
+                        list(map(
+                            lambda x: "[" + x + "]",
+                            re.findall(r"\[(\d+)\]", level_item))
+                        )
+                    )
+                    item_str = property_name + array_index
+                else:
+                    item_str = "['" + level_item + "']"
+                path += item_str
+            exclude_paths.append(path.strip())
+    return exclude_paths, exclude_regex_paths
+
+
+def handle_diff(actual_request_obj, expect_request_obj, operation,
+                target_file_name):
+    exclude_paths, exclude_regex_paths = handle_ignore_node(operation)
+    ignore_order = gr.get_web_info_value("ignore_order", False)
+    # diffs with jsons
+    diff = DeepDiff(actual_request_obj, expect_request_obj,
+                    ignore_order=ignore_order, verbose_level=2,
+                    exclude_paths=exclude_paths,
+                    exclude_regex_paths=exclude_regex_paths)
+    if diff:
+        format_diff = json.dumps(diff, indent=2)
+        message = f'Difference when comparing service request ' \
+                  f'[{operation}] with [{target_file_name}]. ' \
+                  f'\n Difference node:\n {format_diff} \n'
+        raise FlybirdsException(message)
+    log.info(f'compare the service request [{operation}] with '
+             f'[{target_file_name}], the result is the same.')
+
+
+def get_case_response_body(case_id):
+    operation_module = gr.get_value("projectScript").custom_operation
+    get_mock_case_body = getattr(operation_module, "get_mock_case_body")
+    mock_case_body = get_mock_case_body(case_id)
+    if mock_case_body is not None:
+        log.info('[get_case_response_body] successfully get mockCaseBody '
+                 'from custom operation')
+        return mock_case_body
+    log.warn('[get_case_response_body] cannot get mockCaseBody from custom '
+             'operation. Now try to get from the folder mockCaseData.')
+    # read from folder mockCaseData
+    mock_data_path = os.path.join(os.getcwd(), "mockCaseData")
+    all_mock_data = read_json_data(mock_data_path)
+    if all_mock_data.get(case_id):
+        log.info('[get_case_response_body] successfully get mockCaseBody '
+                 'from folder mockCaseData')
+        return all_mock_data.get(case_id)
+    log.warn('[get_case_response_body] cannot get mockCaseBody from folder '
+             'mockCaseData.')
+    return
diff --git a/flybirds/core/plugin/plugins/default/web/page.py b/flybirds/core/plugin/plugins/default/web/page.py
index 153e2240..8ed62fb1 100644
--- a/flybirds/core/plugin/plugins/default/web/page.py
+++ b/flybirds/core/plugin/plugins/default/web/page.py
@@ -3,10 +3,16 @@
 # @Author : hyx
 # @File : page.py
 # @desc : web page implement
+import json
+import time
+from urllib.parse import urlparse
+
 import flybirds.core.global_resource as global_resource
 import flybirds.core.global_resource as gr
 import flybirds.utils.flybirds_log as log
 import flybirds.utils.verify_helper as verify_helper
+from flybirds.core.plugin.plugins.default.web.interception import \
+    get_case_response_body
 from flybirds.utils import dsl_helper
 from flybirds.utils.dsl_helper import is_number
 
@@ -26,12 +32,15 @@ def __init__(self):
 
     @staticmethod
     def init_page():
-        browser = gr.get_value('browser')
-        context = browser.new_context(record_video_dir="videos",
-                                      ignore_https_errors=True)
-        default_timeout = gr.get_web_info_value("default_time_out", 30)
-        context.set_default_timeout(float(default_timeout) * 1000)
+        context = Page.new_browser_context()
+
         page = context.new_page()
+        request_interception = gr.get_web_info_value("request_interception",
+                                                     True)
+        if request_interception:
+            page.route("**/*", handle_route)
+            # request listening events
+            page.on("request", handle_request)
 
         ele_wait_time = gr.get_frame_config_value("wait_ele_timeout", 30)
         page_render_timeout = gr.get_frame_config_value("page_render_timeout",
@@ -40,6 +49,22 @@ def init_page():
         page.set_default_navigation_timeout(float(page_render_timeout) * 1000)
         return page, context
 
+    @staticmethod
+    def new_browser_context():
+        browser = gr.get_value('browser')
+
+        operation_module = gr.get_value("projectScript").custom_operation
+        create_browser_context = getattr(operation_module,
+                                         "create_browser_context")
+        context = create_browser_context(browser)
+        if context is not None:
+            log.info('[new_browser_context] successfully get BrowserContext '
+                     'from custom operation')
+            return context
+        context = browser.new_context(record_video_dir="videos",
+                                      ignore_https_errors=True)
+        return context
+
     def navigate(self, context, param):
         param_dict = dsl_helper.params_to_dic(param, "urlKey")
         url_key = param_dict["urlKey"]
@@ -64,3 +89,53 @@ def cur_page_equal(self, context, param):
             schema_url = global_resource.get_page_schema_url(param)
             target_url = schema_url
         verify_helper.text_equal(target_url, cur_url)
+
+
+def handle_request(request):
+    # interception request handle
+    parsed_uri = urlparse(request.url)
+    operation = parsed_uri.path.split('/')[-1]
+    if operation is not None:
+        interception_request = gr.get_value('interceptionRequest')
+        request_body = interception_request.get(operation)
+
+        if request_body is not None:
+            log.info(
+                f'[handle_request] start cache service：{operation}')
+            current_request_info = {'postData': request.post_data,
+                                    'url': request.url,
+                                    'updateTimeStamp': int(
+                                        round(time.time() * 1000))}
+            interception_request[operation] = current_request_info
+            gr.set_value("interceptionRequest", interception_request)
+
+
+def handle_route(route):
+    abort_domain_list = gr.get_web_info_value("abort_domain_list", [])
+    parsed_uri = urlparse(route.request.url)
+    domain = parsed_uri.hostname
+    if abort_domain_list and domain in abort_domain_list:
+        route.abort()
+        return
+
+    resource_type = route.request.resource_type
+    if resource_type != 'fetch' and resource_type != 'xhr':
+        route.continue_()
+        return
+
+    # mock response data
+    operation = parsed_uri.path.split('/')[-1]
+    mock_case_id = None
+    if operation is not None:
+        interception_values = gr.get_value('interceptionValues')
+        mock_case_id = interception_values.get(operation)
+    if mock_case_id:
+        mock_body = get_case_response_body(mock_case_id)
+        if mock_body:
+            if not isinstance(mock_body, str):
+                mock_body = json.dumps(mock_body)
+            route.fulfill(status=200,
+                          content_type="application/json;charset=utf-8",
+                          body=mock_body)
+    else:
+        route.continue_()
diff --git a/flybirds/core/plugin/plugins/default/web/step.py b/flybirds/core/plugin/plugins/default/web/step.py
index 6417464c..86f8948c 100644
--- a/flybirds/core/plugin/plugins/default/web/step.py
+++ b/flybirds/core/plugin/plugins/default/web/step.py
@@ -10,6 +10,8 @@
     import to_app_home, app_login, app_logout
 from flybirds.core.plugin.plugins.default.step.record import \
     stop_screen_record
+from flybirds.core.plugin.plugins.default.web.interception import \
+    Interception as request_op
 
 __open__ = ["Step"]
 
@@ -103,12 +105,12 @@ def ele_text_equal(cls, context, selector, param_2):
     @classmethod
     def exist_ele(cls, context, selector):
         ele = gr.get_value("plugin_ele")
-        ele.get_ele_locator(selector)
+        ele.ele_exist(context, selector)
 
     @classmethod
     def wait_ele_exit(cls, context, selector):
         ele = gr.get_value("plugin_ele")
-        ele.get_ele_locator(selector)
+        ele.ele_exist(context, selector)
 
     @classmethod
     def ele_not_exit(cls, context, selector):
@@ -164,7 +166,7 @@ def ele_attr_equal(cls, context, selector, param2, param3):
     @classmethod
     def text_attr_equal(cls, context, selector, param2, param3):
         ele = gr.get_value("plugin_ele")
-        ele.is_ele_attr_equal(context, selector, param2, param3)
+        ele.is_text_attr_equal(context, selector, param2, param3)
 
     @classmethod
     def find_child_from_parent(cls, context, p_selector, c_selector):
@@ -192,3 +194,51 @@ def app_login(cls, context, param1, param2):
     @classmethod
     def app_logout(cls, context):
         app_logout(context)
+
+    # -------------------------------------------------------------------------
+    # request interception
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def add_request_body(context, service_str):
+        request_op.add_some_interception_request_body(service_str)
+
+    @staticmethod
+    def remove_request_body(context, service_str):
+        request_op.remove_some_interception_request_body(service_str)
+
+    @staticmethod
+    def clear_all_request_body(context):
+        request_op.clear_interception_request_body()
+
+    # -------------------------------------------------------------------------
+    # request service listening
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def add_request_mock(context, service_str, mock_case_id_str):
+        request_op.add_some_interception_mock(service_str, mock_case_id_str)
+
+    @staticmethod
+    def remover_request_mock(context, service_str):
+        request_op.remove_some_interception_mock(service_str)
+
+    @staticmethod
+    def clear_all_request_mock(context):
+        request_op.clear_interception_mock()
+
+    # -------------------------------------------------------------------------
+    # compare service requests
+    # -------------------------------------------------------------------------
+    @staticmethod
+    def request_compare_from_path(context, operation, target_data_path):
+        request_op.request_compare(operation, target_data_path)
+
+    @staticmethod
+    def request_query_str_compare_from_path(context, operation,
+                                            target_data_path):
+        request_op.request_query_string_compare(operation, target_data_path)
+
+    @staticmethod
+    def request_compare_value(context, operation, target_json_path,
+                              expect_value):
+        request_op.request_compare_value(operation, target_json_path,
+                                         expect_value)
diff --git a/flybirds/core/script_config.py b/flybirds/core/script_config.py
index fe68ba74..e106aa9a 100644
--- a/flybirds/core/script_config.py
+++ b/flybirds/core/script_config.py
@@ -19,13 +19,13 @@ def __init__(self):
         if os.path.exists(params_deal_path):
             params_deal_import_path = "params_deal"
             self.params_deal = __import__(params_deal_import_path)
-        app_operation_path = os.path.join(
-            os.getcwd(), "pscript", "app", "operation.py"
+        custom_operation_path = os.path.join(
+            os.getcwd(), "pscript", "custom_handle", "operation.py"
         )
-        if os.path.exists(app_operation_path):
-            app_operation_import_path = "app.operation"
-            app_module = __import__(app_operation_import_path)
-            self.app_operation = getattr(app_module, "operation")
+        if os.path.exists(custom_operation_path):
+            custom_operation_import_path = "custom_handle.operation"
+            operation_module = __import__(custom_operation_import_path)
+            self.custom_operation = getattr(operation_module, "operation")
         dsl_hook_path = os.path.join(os.getcwd(), "pscript", "dsl", "hook.py")
         if os.path.exists(dsl_hook_path):
             dsl_hook_import_path = "dsl.hook"
diff --git a/flybirds/report/parallel_runner.py b/flybirds/report/parallel_runner.py
index 07d0260d..af1359a4 100644
--- a/flybirds/report/parallel_runner.py
+++ b/flybirds/report/parallel_runner.py
@@ -129,7 +129,7 @@ def dry_run_parsed_cmd(cmd: str) -> str:
     p = Popen(cmd, stdout=PIPE, shell=True)
     out, err = p.communicate()
 
-    return json.loads(out.decode())
+    return json.loads(out.decode('utf-8-sig'))
 
 
 def get_features_num(cmd: str):
diff --git a/flybirds/template/compareData/getRecommendHotelList.json b/flybirds/template/compareData/getRecommendHotelList.json
new file mode 100644
index 00000000..e2e889d0
--- /dev/null
+++ b/flybirds/template/compareData/getRecommendHotelList.json
@@ -0,0 +1,33 @@
+{
+  "cityId": 2,
+  "hotCities": false,
+  "oversea": false,
+  "head": {
+    "cid": "12131017227555019",
+    "ctok": "",
+    "cver": "1.0",
+    "lang": "01",
+    "sid": "6666",
+    "syscode": "PC",
+    "auth": "",
+    "xsid": "",
+    "extension": [
+      {
+        "name": "sdkVersion",
+        "value": "2.34.0"
+      },
+      {
+        "name": "openId",
+        "value": "ce0ae70f-9365"
+      },
+      {
+        "name": "supportWeb_P",
+        "value": "false"
+      },
+      {
+        "name": "scene",
+        "value": "1036"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/flybirds/template/compareData/writecookie.txt b/flybirds/template/compareData/writecookie.txt
new file mode 100644
index 00000000..ef9763cc
--- /dev/null
+++ b/flybirds/template/compareData/writecookie.txt
@@ -0,0 +1,1 @@
+Union=index%26AllianceID%3D4897%26SID%3D155952%26SourceID%3D%26createtime%3D1653126480%26Expires%3D1653731279889&isSec=true&token=10005678
\ No newline at end of file
diff --git a/flybirds/template/config/flybirds_config.json b/flybirds/template/config/flybirds_config.json
index 71750ed3..2f9c460b 100644
--- a/flybirds/template/config/flybirds_config.json
+++ b/flybirds/template/config/flybirds_config.json
@@ -15,7 +15,9 @@
   "web_info": {
     "headless": true,
     "browserType": [],
-    "defaultTimeout": 30
+    "requestInterception": true,
+    "ignoreOrder": false,
+    "abortDomainList": []
   },
   "flow_behave": {
     "beforeRunPage": "restartApp",
diff --git a/flybirds/template/config/schema_url.json b/flybirds/template/config/schema_url.json
index 78de5519..66c5a48f 100644
--- a/flybirds/template/config/schema_url.json
+++ b/flybirds/template/config/schema_url.json
@@ -16,5 +16,6 @@
     "android": "/rn_test/ctrip_flight_list_detail_android/",
     "ios": "/rn_test/ctrip_flight_list_detail_ios/",
     "web": "https://spa6.scrape.center/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx"
-  }
+  },
+  "携程官网": "https://www.ctrip.com/?sid=155952&allianceid=4897&ouid=index"
 }
\ No newline at end of file
diff --git a/flybirds/template/features/steps/steps.py b/flybirds/template/features/steps/steps.py
index 2d726bb0..5a17dcd4 100644
--- a/flybirds/template/features/steps/steps.py
+++ b/flybirds/template/features/steps/steps.py
@@ -5,6 +5,7 @@
 import flybirds.core.dsl.step.element
 import flybirds.core.dsl.step.page
 import flybirds.core.dsl.step.device
+import flybirds.core.dsl.step.request
 
 # import custom step statements
 import pscript.dsl.step.custom_test
diff --git a/flybirds/template/pscript/app/__init__.py b/flybirds/template/pscript/custom_handle/__init__.py
similarity index 100%
rename from flybirds/template/pscript/app/__init__.py
rename to flybirds/template/pscript/custom_handle/__init__.py
diff --git a/flybirds/template/pscript/app/operation.py b/flybirds/template/pscript/custom_handle/operation.py
similarity index 71%
rename from flybirds/template/pscript/app/operation.py
rename to flybirds/template/pscript/custom_handle/operation.py
index b2207aa6..9d4f6f78 100644
--- a/flybirds/template/pscript/app/operation.py
+++ b/flybirds/template/pscript/custom_handle/operation.py
@@ -64,3 +64,29 @@ def login(user_name, password):
 
 def logout():
     pass
+
+
+def get_mock_case_body(mock_case_id):
+    """
+    custom get mockCase response body
+
+    :param mock_case_id: unique key for get mock data
+    """
+    mock_case_body = None
+    # here add something to get mockCase response body...
+    return mock_case_body
+
+
+def create_browser_context(browser):
+    """
+    custom creates a new browser context.
+    :param browser: the browser instance
+    """
+    context = None
+    # here add something to create BrowserContext...
+    # For example, adding a 'local' parameter when create.
+    #
+    # context = browser.new_context(record_video_dir="videos",
+    #                               ignore_https_errors=True,
+    #                               locale="en")
+    return context
diff --git a/flybirds/template/pscript/other_platform/__init__.py b/flybirds/template/pscript/other_platform/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/flybirds/template/pscript/other_platform/custom.py b/flybirds/template/pscript/other_platform/custom.py
deleted file mode 100644
index 8a10ba5f..00000000
--- a/flybirds/template/pscript/other_platform/custom.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Custom Step statement implementation module
-"""
-
-
-def custom_function():
-    pass
diff --git a/flybirds/utils/dsl_helper.py b/flybirds/utils/dsl_helper.py
index 8746ea8c..4745459c 100644
--- a/flybirds/utils/dsl_helper.py
+++ b/flybirds/utils/dsl_helper.py
@@ -166,7 +166,10 @@ def wrapper_func(*args, **kwargs):
                     continue
             v = replace_str(v)
             if 'selector' in k:
-                v = gr.get_ele_locator(v)
+                selector_str = v
+                ele_key = v.split(',')[0]
+                ele_value = gr.get_ele_locator(ele_key)
+                v = selector_str.replace(ele_key, ele_value, 1)
             kwargs[k] = v
         func(*args, **kwargs)
         # Do something after the function.
diff --git a/flybirds/utils/file_helper.py b/flybirds/utils/file_helper.py
index 4755348d..4beb06e3 100644
--- a/flybirds/utils/file_helper.py
+++ b/flybirds/utils/file_helper.py
@@ -4,6 +4,7 @@
 """
 import json
 import os
+import re
 from json import JSONDecodeError
 
 from flybirds.utils import flybirds_log as log
@@ -25,7 +26,7 @@ def get_json_from_file_path(path):
     result = None
     # noinspection PyBroadException
     try:
-        f = open(path, "r", errors='ignore')
+        f = open(path, "r", errors='ignore', encoding="utf-8")
         json_str = f.read().strip().replace('\r\n', '\\r\\n')
         result = json.loads(json_str, strict=False)
     except JSONDecodeError:
@@ -178,3 +179,27 @@ def update_json_data(json_path, key, value):
 
     with open(json_path, "w") as jsonFile:
         json.dump(data, jsonFile, indent=2, separators=(',', ': '))
+
+
+def read_file_from_path(path):
+    """
+    Get the content of the file by path
+    """
+    with open(path, 'r') as f:
+        data = f.read()
+    return data
+
+
+def read_json_data(root_dir_path):
+    all_json_data = {}
+    if not os.path.exists(root_dir_path):
+        log.warn(f'[read_json_data] does not exists path:{root_dir_path}')
+        return all_json_data
+    for main_dir, dirs, file_name_list in os.walk(root_dir_path):
+        for file in file_name_list:
+            file_path = os.path.join(main_dir, file)
+            if re.search(r"\.json", str(file_path)) is not None:
+                json_data = get_json_from_file_path(file_path)
+                for key, value in json_data.items():
+                    all_json_data[key] = value
+    return all_json_data
diff --git a/flybirds/utils/launch_helper.py b/flybirds/utils/launch_helper.py
index 25ccfb5f..ef067eba 100644
--- a/flybirds/utils/launch_helper.py
+++ b/flybirds/utils/launch_helper.py
@@ -19,7 +19,7 @@ def login():
     need_login = gr.get_flow_behave_value("before_run_login", False)
     log.info("before_run_login:{}".format(need_login))
     if need_login:
-        schema_goto_module = gr.get_value("projectScript").app_operation
+        schema_goto_module = gr.get_value("projectScript").custom_operation
         login = getattr(schema_goto_module, "login")
         if not (login is None):
             login_user = gr.get_app_config_value("default_user")


Test patch:
diff --git a/flybirds/template/features/test/web/request_interception/web_cn_request_cache.feature b/flybirds/template/features/test/web/request_interception/web_cn_request_cache.feature
new file mode 100644
index 00000000..0977fb9d
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_cn_request_cache.feature
@@ -0,0 +1,20 @@
+  # language: zh-CN
+  功能: 缓存服务请求
+
+
+    场景:验证缓存服务请求
+    假如 缓存服务请求[getRecommendHotelList]
+    假如 缓存服务请求[writecookie]
+    而且 跳转页面到[携程官网]
+    那么 等待[5]秒
+    而且 移除请求缓存[getRecommendHotelList]
+#    而且 移除请求缓存[writecookie]
+    而且 移除所有请求缓存
+
+
+    场景:验证缓存服务请求--同时传入多个参数
+    假如 缓存服务请求[getRecommendHotelList,writecookie]
+    而且 跳转页面到[携程官网]
+    那么 等待[5]秒
+    而且 移除请求缓存[getRecommendHotelList,writecookie]
+
diff --git a/flybirds/template/features/test/web/request_interception/web_cn_request_compare.feature b/flybirds/template/features/test/web/request_interception/web_cn_request_compare.feature
new file mode 100644
index 00000000..a61a0edf
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_cn_request_compare.feature
@@ -0,0 +1,19 @@
+  # language: zh-CN
+  功能: 验证比较服务请求
+
+
+    场景: json类型服务请求比对
+    假如 缓存服务请求[getRecommendHotelList]
+    而且 跳转页面到[携程官网]
+    那么 等待[5]秒
+    而且 验证服务[getRecommendHotelList]的请求参数[head.syscode]与[PC]一致
+    而且 验证服务[getRecommendHotelList]的请求参数[$.cityId]与[2]一致
+    而且 验证服务[getRecommendHotelList]的请求参数[cityId]与[2]一致
+    而且 验证服务请求[getRecommendHotelList]与[compareData/getRecommendHotelList.json]一致
+
+
+    场景: 非json类型服务请求比对
+    假如 缓存服务请求[writecookie]
+    而且 跳转到[携程官网]
+    那么 等待[5]秒
+    而且 验证服务非json请求[writecookie]与[compareData/writecookie.txt]一致
diff --git a/flybirds/template/features/test/web/request_interception/web_cn_request_mock.feature b/flybirds/template/features/test/web/request_interception/web_cn_request_mock.feature
new file mode 100644
index 00000000..1f7feea4
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_cn_request_mock.feature
@@ -0,0 +1,14 @@
+  # language: zh-CN
+  功能: 监听并mock服务请求
+
+
+    场景: 服务监听与mock
+    假如 监听服务[movie]绑定MockCase[4245512]
+#    假如 监听服务[movie,xxx]绑定MockCase[4245512,xxxxxxx]
+    当   跳转页面到[列表页]
+    那么 等待[10]秒
+    而且 移除服务监听[movie]
+#    而且 移除服务监听[movie,xxx]
+    而且 移除所有服务监听
+
+
diff --git a/flybirds/template/features/test/web/request_interception/web_en_request_cache.feature b/flybirds/template/features/test/web/request_interception/web_en_request_cache.feature
new file mode 100644
index 00000000..f3675273
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_en_request_cache.feature
@@ -0,0 +1,20 @@
+ # language: en
+ Feature: cache service requests
+
+
+   Scenario: Verify cached service requests
+     Given cache service request [getRecommendHotelList]
+     Given cache service request [writecookie]
+     And go to url[携程官网]
+     Then wait[5]seconds
+     And remove service request cache [getRecommendHotelList]
+#     And remove service request cache [writecookie]
+     And remove all service request caches
+
+
+   Scenario: Verify cached service requests--pass in multiple parameters
+     Given cache service request [getRecommendHotelList,writecookie]
+     And go to url[携程官网]
+     Then wait[5]seconds
+     And remove service request cache [getRecommendHotelList,writecookie]
+
diff --git a/flybirds/template/features/test/web/request_interception/web_en_request_compare.feature b/flybirds/template/features/test/web/request_interception/web_en_request_compare.feature
new file mode 100644
index 00000000..0b62454a
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_en_request_compare.feature
@@ -0,0 +1,19 @@
+  # language: en
+  Feature: Validate and compare service requests
+
+
+    Scenario: json type service request comparison
+      Given cache service request [getRecommendHotelList]
+      And go to url[携程官网]
+      Then wait[5]seconds
+      And  service request [getRecommendHotelList] request parameter [head.syscode] is [PC]
+      And  service request [getRecommendHotelList] request parameter [$.cityId] is [2]
+      And  service request [getRecommendHotelList] request parameter [cityId] is [2]
+      And compare service request [getRecommendHotelList] with json file [compareData/getRecommendHotelList.json]
+
+
+    Scenario: Non-json type service request comparison
+      Given cache service request [writecookie]
+      And go to url[携程官网]
+      Then wait[5]seconds
+      And compare service non-json request [writecookie] with non-json file [compareData/writecookie.txt]
diff --git a/flybirds/template/features/test/web/request_interception/web_en_request_mock.feature b/flybirds/template/features/test/web/request_interception/web_en_request_mock.feature
new file mode 100644
index 00000000..b268a8c0
--- /dev/null
+++ b/flybirds/template/features/test/web/request_interception/web_en_request_mock.feature
@@ -0,0 +1,13 @@
+  # language: en
+  Feature: Listening and mocking service requests
+
+    Scenario: Service requests Listening and mock
+      Given listening service [movie] bind mockCase[4245512]
+#      Given listening service [movie,xxx] bind mockCase[4245512,xxxxxxx]
+      And go to url[列表页]
+      Then wait[10]seconds
+      And remove service listener [movie]
+#      And remove service listener [movie,xxx]
+      And remove all service listeners
+
+
diff --git a/flybirds/template/interfaceIgnoreConfig/test.json b/flybirds/template/interfaceIgnoreConfig/test.json
new file mode 100644
index 00000000..9c585fb5
--- /dev/null
+++ b/flybirds/template/interfaceIgnoreConfig/test.json
@@ -0,0 +1,9 @@
+{
+  "getRecommendHotelList": [
+    "head.cid",
+    "regex: root\\['head'\\]\\['extension'\\]\\[\\d+\\]\\['value'\\]"
+  ],
+  "writecookie": [
+    "token"
+  ]
+}
diff --git a/flybirds/template/mockCaseData/mock_test.json b/flybirds/template/mockCaseData/mock_test.json
new file mode 100644
index 00000000..a57bd517
--- /dev/null
+++ b/flybirds/template/mockCaseData/mock_test.json
@@ -0,0 +1,57 @@
+{
+  "4245512": {
+    "count": 101,
+    "results": [
+      {
+        "id": 1,
+        "name": "测试电影——01",
+        "alias": "this is mock data",
+        "cover": "https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c",
+        "categories": [
+          "剧情",
+          "爱情"
+        ],
+        "published_at": "1993-07-26",
+        "minute": 171,
+        "score": 9.5,
+        "regions": [
+          "中国内地",
+          "中国香港"
+        ]
+      },
+      {
+        "id": 9,
+        "name": "测试电影——02",
+        "alias": "The Truman Show",
+        "cover": "https://p0.meituan.net/movie/8959888ee0c399b0fe53a714bc8a5a17460048.jpg@464w_644h_1e_1c",
+        "categories": [
+          "剧情",
+          "科幻"
+        ],
+        "published_at": null,
+        "minute": 103,
+        "score": 9.0,
+        "regions": [
+          "美国"
+        ]
+      },
+      {
+        "id": 10,
+        "name": "test-狮子王",
+        "alias": "The Lion King",
+        "cover": "https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c",
+        "categories": [
+          "动画",
+          "歌舞",
+          "冒险"
+        ],
+        "published_at": "1995-07-15",
+        "minute": 89,
+        "score": 9.0,
+        "regions": [
+          "美国"
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file