docs: 完善PRD文档,新增咨询人弹窗和咨询人档案详情章节,更新页面截图和截图规范

This commit is contained in:
冯普
2026-04-16 16:40:26 +08:00
parent da704d8c83
commit 29afceb3d0
628 changed files with 169600 additions and 396 deletions
+237 -134
View File
@@ -2,7 +2,7 @@
> **所属端**:员工端 APP
> **模块路径**`employee-app/health-consult/`
> **最后更新**2026-04-14
> **最后更新**2026-04-15
---
@@ -15,14 +15,14 @@
```
健康咨询首页
├── 找专家
│ ├── 综合搜索 → 专家主页 / 医院详情 / 专家列表
│ ├── 综合搜索 → 搜索结果 → 专家主页 / 医院详情 / 专家列表
│ ├── [按医院] → 医院详情 → 专家主页
│ ├── [按科室] → 专家列表 → 专家主页
│ └── [按疾病] → 专家列表 → 专家主页
├── AI 小助手
├── 咨询小助手
└── 我的咨询
├── [咨询专家] → 图文咨询详情 / 视频咨询详情
└── [咨询小助手] → AI 小助手聊天
└── [咨询小助手] → 小助手咨询记录
专家主页
├── [图文咨询] → 咨询须知弹窗 → 选择咨询人 → 图文咨询聊天 → 结束咨询确认弹窗 → 评价医生
@@ -48,34 +48,37 @@ stateDiagram-v2
### 页面清单
| # | 文件 | 名称 | 类型 |
|---|------|------|------|
| 1 | index.html | 模块首页 | 页面 |
| 2 | find-expert.html | 找专家 | 页面 |
| 3 | search.html | 综合搜索 | 页面 |
| 4 | expert-list.html | 专家列表 | 页面 |
| 5 | hospital-detail.html | 医院详情 | 页面 |
| 6 | expert-profile.html | 专家主页 | 页面 |
| 7 | expert-reviews.html | 用户评价列表 | 页面 |
| 8 | (内嵌于 expert-profile.html | 咨询须知弹窗 | 弹窗 |
| 9 | select-person.html | 选择咨询人 | 页面 |
| 10 | add-health-record.html | 新增健康档案 | 页面 |
| 11 | text-chat.html | 图文咨询聊天 | 页面 |
| 12 | select-report-dialog.html | 选择体检报告弹窗 | 弹窗 |
| 13 | end-consult-dialog.html | 结束咨询确认弹窗 | 弹窗 |
| 14 | text-chat-history.html | 图文咨询历史对话 | 页面 |
| 15 | video-booking.html | 视频咨询预约 | 页面 |
| 16 | video-success.html | 预约成功 | 页面 |
| 17 | video-detail-confirming.html | 视频咨询详情 - 待确认 | 页面 |
| 18 | video-detail-pending.html | 视频咨询详情 - 待开始 | 页面 |
| 19 | video-detail-done.html | 视频咨询详情 - 已完成 | 页面 |
| 20 | video-detail-cancelled.html | 视频咨询详情 - 已取消 | 页面 |
| 21 | video-detail-rejected.html | 视频咨询详情 - 已拒绝 | 页面 |
| 22 | rate-doctor.html | 评价医生 | 页面 |
| 23 | my-records.html | 我的咨询 | 页面 |
| 24 | record-detail.html | 咨询详情 | 页面 |
| 25 | assistant-chat.html | AI 小助手聊天 | 页面 |
| 26 | assistant-chat-history.html | 小助手历史对话 | 页面 |
| # | 文件 | 名称 | 类型 |
|---|------|--------------|------|
| 1 | index.html | 模块首页 | 页面 |
| 2 | find-expert.html | 找专家 | 页面 |
| 3 | search.html | 综合搜索 | 页面 |
| 4 | search.html 结果态) | 搜索结果 | 页面状态 |
| 5 | expert-list.html | 专家列表 | 页面 |
| 6 | hospital-detail.html | 医院详情 | 页面 |
| 7 | expert-profile.html | 专家主页 | 页面 |
| 8 | expert-reviews.html | 用户评价列表 | 页面 |
| 9 | (内嵌于 expert-profile.html | 咨询须知弹窗 | 弹窗 |
| 10 | select-person.html | 选择咨询人 | 页面 |
| 11 | (内嵌于 select-person.html | 新增咨询人弹窗 | 弹窗 |
| 12 | add-health-record.html | 新增健康档案 | 页面 |
| 13 | consult-person-detail.html | 咨询人档案详情 | 页面 |
| 14 | text-chat.html | 图文咨询聊天 | 页面 |
| 15 | select-report-dialog.html | 选择体检报告弹窗 | 弹窗 |
| 16 | end-consult-dialog.html | 结束咨询确认弹窗 | 弹窗 |
| 17 | text-chat-history.html | 图文咨询历史对话 | 页面 |
| 18 | video-booking.html | 视频咨询预约 | 页面 |
| 19 | video-success.html | 预约成功 | 页面 |
| 20 | video-detail-confirming.html | 视频咨询详情 - 待确认 | 页面 |
| 21 | video-detail-pending.html | 视频咨询详情 - 待开始 | 页面 |
| 22 | video-detail-done.html | 视频咨询详情 - 已完成 | 页面 |
| 23 | video-detail-cancelled.html | 视频咨询详情 - 已取消 | 页面 |
| 24 | video-detail-rejected.html | 视频咨询详情 - 已拒绝 | 页面 |
| 25 | rate-doctor.html | 评价医生 | 页面 |
| 26 | my-records.html | 我的咨询 | 页面 |
| 27 | record-detail.html | 咨询详情 | 页面 |
| 28 | assistant-chat.html | 咨询小助手 | 页面 |
| 29 | assistant-chat-history.html | 小助手历史对话 | 页面 |
---
@@ -88,21 +91,22 @@ stateDiagram-v2
**功能说明**
模块入口页,提供三大功能快捷入口(找专家、AI 小助手、我的咨询)和推荐专家列表。专家卡片展示姓名、职称、医院科室、擅长标签、评分、咨询次数、回复率。
模块入口页,提供三大功能快捷入口(找专家、咨询小助手、我的咨询)和推荐专家列表。专家卡片展示姓名、职称、医院科室、擅长标签、评分、咨询次数、回复率。
**业务规则**
- 推荐专家由后端根据评分、咨询量等维度综合排序
- 推荐专家由后端根据咨询量 -> 评分 ->回复率3个维度综合排序,3个排序因子都相同时使用随机策略
- 首页推荐专家至多只显示6个,没有专家使用占位图
- 每张专家卡片展示不超过 3 个擅长标签
**交互说明**
- 点击「找专家」跳转到找专家页
- 点击「AI 小助手」跳转到 AI 小助手聊天页
- 点击「小助手」跳转到 小助手聊天页
- 点击「我的咨询」跳转到我的咨询记录页
- 点击「查看全部」跳转到找专家页
- 点击专家卡片或卡片上的「咨询」按钮,跳转到专家主页
- 导航栏返回 → 员工端首
- 导航栏返回 → 精确返回来源
---
@@ -113,27 +117,27 @@ stateDiagram-v2
**功能说明**
三维度查找专家:按医院、按科室、按疾病。顶部搜索栏点击后跳转到综合搜索页。
三维度查找专家:按医院、按科室、按疾病。顶部搜索栏点击后跳转到综合搜索页。
- **全部医院**:医院卡片列表,展示名称、等级、距离、专家数、地址
- **全部科室**:左侧一级分类 + 右侧二级科室网格,每项展示科室名和专家数
- **全部疾病**横向分类 Tab + 疾病列表,每项展示疾病名和专家数
- **医院**:医院卡片列表,展示名称、等级、距离、专家数、地址
- **科室**:左侧一级分类 + 右侧二级科室网格,每项展示科室名和专家数
- **疾病**左侧科室名称 + 右侧科室关联的疾病名称列表,每项展示疾病名和专家数
**业务规则**
- 搜索基于 ElasticSearch 模糊匹配,可搜医院、科室、疾病、医生姓名
- 科室体系:6 个一级分类(内科、外科、妇产科、儿科、眼耳鼻喉、皮肤科),每级含多个二级科室
- 疾病体系:6 大类(心血管、内分泌、骨骼肌肉、神经系统、消化系统、呼吸系统)
- 医院距离基于用户当前定位计算
- 顶部搜索框跳转至综合搜索二级页面
- 按医院:下拉分页(每页10条)展示所有医院信息,取当前用户位置信息按距离进行升序,位置信息获取失败按医院等级排序
- 按科室:左侧展示所有一级科室分类,右侧展示该分类下二级科室网格并按专家数量倒序
- 按疾病:左侧展示所有一级科室分类,右侧展示该分类下所有关联的疾病信息并按擅长此疾病的专家数量倒序
**交互说明**
- 点击搜索栏跳转到综合搜索页
- 顶部 Tab 切换「全部医院 / 全部科室 / 全部疾病」三个面板
- 顶部 Tab 切换「医院 / 科室 / 疾病」三个面板
- 点击医院卡片跳转到医院详情页
- 科室面板:点击左侧一级分类联动切换右侧二级科室;点击二级科室项跳转到专家列表页
- 疾病面板:点击横向分类 Tab 切换疾病列表;点击疾病项跳转到专家列表页
- 导航栏返回 → 健康咨询首页
- 疾病面板:点击左侧一级分类联动切换右侧关联疾病;点击疾病项跳转到专家列表页
- 导航栏返回 → 健康咨询首页index.html
---
@@ -144,98 +148,126 @@ stateDiagram-v2
**功能说明**
综合搜索二级页面,支持按关键字搜索所有相关信息。搜索结果按专家、医院、科室、疾病四个分类混合展示。
综合搜索二级页面,支持按关键字搜索所有相关信息。
- **默认态**:展示搜索历史标签和热门搜索标签
- **结果态**按分类展示匹配结果,专家和医院分类带"查看全部"入口
- **空态**:无匹配结果时展示空状态提示
- **默认态**:展示搜索历史标签
- **搜索态**输入关键字或点击历史搜索标签后,跳转到搜索结果页展示匹配结果
**业务规则**
- 搜索基于 ElasticSearch 全文模糊匹配,同时搜索专家姓名、医院名称、科室名称、疾病名称
- 搜索历史本地存储,最多保留 10 条
- 热门搜索由后端配置,定期更新
- 搜索基于用户输入的关键字全文模糊匹配,同时搜索专家姓名、医院名称、科室名称、疾病名称
- 搜索历史移动端本地存储,最多保留 10 条
**交互说明**
- 输入框自动聚焦,输入文字后实时展示搜索结果
- 输入框自动聚焦,输入文字后按搜索或回车跳转到搜索结果
- 输入框右侧清除按钮,点击清空输入并回到默认态
- 点击搜索历史/热门搜索标签,自动填入关键字并触发搜索
- 点击搜索历史搜索标签,跳转到搜索结果页
- 点击"清空"清除所有搜索历史
- 点击专家卡片跳转到专家主页
- 点击医院卡片跳转到医院详情页
- 点击科室/疾病标签跳转到专家列表页
- 点击"取消"返回找专家页
---
### 2.4 专家列表(expert-list.html
### 2.4 搜索结果(search.html 结果态)
![页面截图](screenshots/search-result.jpg)
**功能说明**
综合搜索的结果展示页面。顶部保留搜索栏(显示当前搜索关键词,支持修改后重新搜索)。搜索结果按专家、医院、科室、疾病四个分类混合展示,每个分类默认展示前几条,专家和医院分类带"查看全部"入口。无匹配结果时展示空状态提示。
**业务规则**
- 搜索基于输入的管检词模糊匹配,同时搜索专家姓名、医院名称、科室名称、疾病名称
- 各个分类的搜索结果均按关键词的匹配度排序,匹配度最高地排在前
- 无结果时展示空状态提示"未找到相关结果"
**交互说明**
- 搜索栏显示当前关键词,可修改后重新搜索(刷新当前页面结果)
- 输入框右侧清除按钮,点击清空输入内容
- 点击专家卡片跳转到专家主页
- 点击医院卡片跳转到医院详情页
- 点击科室/疾病标签跳转到专家列表页
- 点击专家/医院分类的"查看全部"跳转到对应的完整列表
- 点击"取消"返回综合搜索页
- 导航栏返回 → 综合搜索页
---
### 2.5 专家列表(expert-list.html
![页面截图](screenshots/expert-list.jpg)
**功能说明**
展示特定分类(科室/疾病)下的专家列表。根据 URL 参数 `type`dept/disease)和 `name` 动态切换页面上下文:
展示特定分类(医院/科室/疾病)下的专家列表。根据 URL 参数 `type`hospital/dept/disease)和 `name` 动态切换页面上下文:
- 科室维度(type=hospital):导航标题"医院专家",头部显示医院名称
- 科室维度(type=dept):导航标题"科室专家",头部显示科室名称
- 疾病维度(type=disease):导航标题"疾病专家",头部显示疾病名称,专家卡片展示与该疾病相关的擅长标签
支持排序(好评优先 / 热度优先 / 回复率)和按姓名搜索过滤。
支持排序(咨询量/评分/回复率)和按姓名搜索过滤。
**业务规则**
- 默认按好评优先排序
- 搜索为前端实时过滤,匹配医生姓名
- 支持咨询量(热度)/评分 /回复率3个维度排序,默认根据热度排序
- 下拉分页加载,搜索匹配医生姓名
- 无结果时展示空状态提示
- 页面通过 URL 参数区分来源,同一页面服务科室和疾病种入口
- 页面通过 URL 参数区分来源,同一页面服务医院、科室和疾病种入口
**交互说明**
- 点击排序按钮切换排序方式,高亮当前选中项
- 搜索输入框输入关键词实时过滤卡片列表
- 点击专家卡片跳转到专家主页
- 导航栏返回 → 找专家
- 导航栏返回 → 精确返回来源
---
### 2.5 医院详情(hospital-detail.html
### 2.6 医院详情(hospital-detail.html
![页面截图](screenshots/hospital-detail.jpg)
**功能说明**
展示医院信息(实景图、名称、等级、地址、距离、导航按钮)、医院简介(支持展开/收起),以及本院专家列表(排序 + 搜索,逻辑同专家列表页)。底部展示用户评价区域。
展示医院信息(图、名称、等级、地址、距离、导航按钮)、医院简介(支持展开/收起),以及本院专家列表(排序 + 搜索,逻辑同专家列表页)。底部展示用户评价区域。
**业务规则**
- 导航按钮调用地图导航(原型仅模拟提示)
- 简介默认收起,超长文本截断
- 专家列表为两列网格布局
- 简介默认收起,超长文本截断,默认显示3行
- 专家列表为两列网格布局,至多显示6个,排序规则使用咨询量 -> 评分2个维度综合排序,2个排序因子都相同时使用随机策略
- 用户评价列表按时间倒序展示本院下医生的评价信息,默认只显示四条
**交互说明**
- 点击导航按钮提示"正在为您导航..."(原型演示)
- 点击导航按钮提示"选择打开手上的地图软件"(原型演示)
- 点击展开/收起按钮切换简介文本显示
- 排序按钮切换专家列表排序方式
- 点击专家卡片跳转到专家主页
- 点击全部专家跳转至专家列表页面(医院专家)
- 点击用户评价-查看全部跳转至用户评价列表页(按医院筛选)
- 导航栏返回 → 找专家页
---
### 2.6 专家主页(expert-profile.html
### 2.7 专家主页(expert-profile.html
![页面截图](screenshots/expert-profile.jpg)
**功能说明**
专家个人详情页。展示头像、姓名、职称、科室、所属医院,统计数据(综合评价、24h 回复率、总咨询量),医生简介、擅长方向标签,以及最新 3 条用户评价。提供图文咨询和视频咨询两个入口。
专家个人详情页。展示头像、姓名、职称、科室、所属医院,统计数据(综合评价、24h回复率、总咨询量),医生简介、擅长方向标签,以及最新 3 条用户评价。提供图文咨询和视频咨询两个入口。
**业务规则**
- 点击任一咨询按钮,先弹出「咨询须知」弹窗,用户点击「同意并开始咨询」后才进入对应流程
- 图文咨询确认后跳转到选择咨询人页;视频咨询确认后进入预约选时
- 返回按钮根据来源页精确返回(从首页进入则返回首页,从专家列表进入则返回专家列表,其他情况返回上一页)
- 24h回复率 = 专家首次回复在24小时内的图文咨询数 / 图文咨询总数 × 100%
- 医生简介和职业经历默认是收起状态,收起状态下至多显示3行,点击展开查看全部
**交互说明**
@@ -247,7 +279,7 @@ stateDiagram-v2
---
### 2.7 用户评价列表(expert-reviews.html
### 2.8 用户评价列表(expert-reviews.html
![页面截图](screenshots/expert-reviews.jpg)
@@ -259,11 +291,14 @@ stateDiagram-v2
**业务规则**
- 评价按时间倒序排列
- 评价标签由系统预设:专业、耐心、回复及时、负责任、个性化、实用
- 评价标签由系统预设:专业、耐心、回复及时、负责任、个性化、实用...,评价标签由服务端根据评价内容自动提取
- 匿名评价不展示用户名称和图像,统一使用默认图像以及“匿名用户”
- 数据过滤:从医院主页进入显示该医院下所有专家的评价,从专家主页进入显示该专家的评价
- 评价列表使用下拉分页加载
**交互说明**
- 导航栏返回 → 专家主
- 导航栏返回 → 精确返回来源
---
@@ -271,15 +306,14 @@ stateDiagram-v2
### 3.1 咨询须知弹窗(consult-notice-dialog.html
![页面截图](screenshots/consult-notice-dialog.jpg)
**功能说明**
发起咨询前的告知弹窗,底部滑出样式。所有咨询入口(图文/视频)必须先展示此弹窗,用户同意后才能进入实际咨询流程。
**须知内容:**
1. 本平台提供在线健康咨询服务,不替代线下就医
2. 请如实描述症状和病史,以便医生准确判断
3. 医生将在 24 小时内回复(图文)/ 按预约时间进行(视频)
4. 如遇紧急情况,请立即拨打 120
1. 须知内容从接口获取,在后台统一配置,图文咨询和视频咨询须知内容不同
**交互说明**
@@ -299,11 +333,12 @@ stateDiagram-v2
**业务规则**
- 员工开启一个新的咨询前可选当前咨询人以及咨询人档案信息,也可跳过该流程直接进入到咨询对话页
- 咨询人卡片展示:头像、姓名、年龄、性别、身高、体重、与本人关系标签
- 有档案的咨询人:卡片下方展开档案列表,每条档案显示档案名称、问题摘要、创建时间
- 无档案的咨询人:卡片独立展示,无档案区域
- 选中档案时自动高亮所属咨询人
-选择档案时仍可发送咨询(仅发送咨询人基本信息)
-创建咨询人或没有咨询档案前不能发起咨询,前端需要提示先创建咨询人或创建咨询档案
**交互说明**
@@ -311,12 +346,56 @@ stateDiagram-v2
- 点击档案条目选中该档案,同时高亮所属咨询人
- 点击「+ 新增健康档案」跳转到新增健康档案页
- 点击「+ 新增咨询人」进入新增咨询人流程
- 点击「发送咨询将咨询人信息和档案发送到对话框并返回
- 导航栏返回 → 上一
- 点击「确定」开启一个新的图文咨询将咨询人信息和档案发送到对话框并返回
- 点击「快速咨询」直接进入到咨询对话
- 导航栏返回 → 精确返回来源页
---
### 3.3 新增健康档案(add-health-record.html
### 3.3 新增咨询人弹窗(内嵌于 select-person.html
![页面截图](screenshots/add-person-dialog.jpg)
**功能说明**
弹窗表单,从选择咨询人页底部「+ 新增咨询人」按钮触发。用于为当前用户添加家属或其他咨询人,表单分为基本信息、生活习惯、身体情况三个区块。保存后新咨询人卡片自动插入选择咨询人列表。
**业务规则**
- 基本信息所有内容均为必填项,保存时校验
- 性别默认选中"男"
- 生活习惯各项均有默认选中值(吸烟:否、饮酒:否、饮食:清淡、睡眠:良好)
- 保存成功后弹窗关闭,新咨询人卡片出现在列表中(无档案状态)
- 底部展示温馨提示:咨询人信息用于平台审核,不会外传.....
**交互说明**
- 点击右上角关闭按钮或遮罩层区域关闭弹窗
- 性别、生活习惯各项通过按钮组单选切换
- 点击「保存」校验姓名后保存并关闭弹窗
**表单字段表**
| 字段 | 类型 | 必填 | 规则 |
|------|------|----|----------------------------|
| 姓名 | 文本 | 是 | 请输入姓名 |
| 性别 | 按钮组单选 | 是 | 男 / 女,默认"男" |
| 出生日期 | 日期选择 | 是 | date 类型 |
| 与本人关系 | 文本 | 是 | 如:配偶、朋友、父母等 |
| 是否吸烟 | 按钮组单选 | 否 | 否 / 偶尔 / 是,默认"否" |
| 是否饮酒 | 按钮组单选 | 否 | 否 / 偶尔 / 是,默认"否" |
| 饮食喜好 | 按钮组单选 | 否 | 清淡 / 均衡 / 偏油腻 / 偏辛辣,默认"清淡" |
| 睡眠质量 | 按钮组单选 | 否 | 良好 / 一般 / 较差,默认"良好" |
| 身高(cm) | 数字 | 否 | 如:170 |
| 体重(kg) | 数字 | 否 | 如:65 |
| 过敏史 | 文本 | 否 | 如:青霉素过敏 |
| 遗传史 | 文本 | 否 | 如:高血压家族史 |
| 既往史 | 文本 | 否 | 如:2020年阑尾炎手术 |
| 其他疾病 | 文本 | 否 | 如:慢性胃炎 |
---
### 3.4 新增健康档案(add-health-record.html
![页面截图](screenshots/add-health-record.jpg)
@@ -327,7 +406,7 @@ stateDiagram-v2
**业务规则**
- 档案名称和问题描述为必填项
- 基本信息所有内容为必填项
- 上传图片仅自己和医生可见,最多 9 张
- 选择"是否去医院就诊过"为"是"时,展开医院名称、科室、疾病名称三个字段
- 保存后档案关联到当前咨询人
@@ -356,33 +435,57 @@ stateDiagram-v2
---
### 3.4 图文咨询聊天(text-chat.html
### 3.5 咨询人档案详情(consult-person-detail.html
![页面截图](screenshots/consult-person-detail.jpg)
**功能说明**
三级页面,展示咨询人的完整健康档案信息。页面分为四个区块:个人信息头部、基础体检数据、生活习惯、健康档案详情。供用户在咨询前查看或确认咨询人的健康状况。
**业务规则**
- 头部展示咨询人头像、姓名、与本人关系标签、年龄/性别/身高/体重
- 基础数据卡片以两列网格展示:身高、体重、BMI、血压、血糖、心率
- 生活习惯以标签形式展示,绿色标签表示良好习惯,橙色标签表示需关注项
- 健康档案卡片展示:问题描述、持续时间、检查资料(图片)、就诊经历(医院/科室/疾病名称)、期望帮助
**交互说明**
- 导航栏返回 → 上一页
---
### 3.6 图文咨询聊天(text-chat.html
![页面截图](screenshots/text-chat.jpg)
**功能说明**
医生的实时图文聊天界面。聊天头部展示医生头像、姓名、在线状态和「结束咨询」按钮。底部输入区含 + 按钮(展开功能面板)、消息输入框、发送按钮。功能面板提供 5 项操作:图片、拍摄、体检报告、选择档案、结束咨询
发起图文咨询后医生的实时图文聊天界面。聊天头部展示医生头像、姓名、在线状态和「结束咨询」按钮。底部输入区含 + 按钮(展开功能面板)、消息输入框、发送按钮。功能面板提供 6 项操作:图片、拍摄、体检报告、选择档案、文件、位置
**业务规则**
- 图文咨询有效期为 24 小时
- 消息发送后 HTML 转义防 XSS
- 结束咨询需通过二次确认弹窗
- 本页无导航栏返回按钮,只能通过结束咨询离开
- 距专家最后一次回复时间超过7天的且状态是进行中的咨询系统自动置为已完成状态(已结束,待评价)
- 超过7天没有专家回复的图文咨询系统自动置为超时状态
**交互说明**
- 点击发送按钮或按 Enter 发送消息,追加到消息列表
- 点击 + 按钮展开/收起功能面板
- 功能面板点击「体检报告」打开选择体检报告弹窗
- 功能面板点击「选择档案」跳转到选择咨询人页
- 点击头部「结束咨询」按钮或功能面板中的「结束咨询」,弹出结束咨询确认弹窗,确认后跳转到评价医生页
- 功能面板点击「图片」选择系统相册中的图片发送至聊天框
- 功能面板点击「拍摄」可拍照或录制视频发送至聊天框
- 功能面板点击「体检报告」打开选择体检报告弹窗,选择咨询人对应年份的体检报告点击发送将体检报告以卡片形式发送至对话框
- 功能面板点击「选择档案」再次发送咨询前填写的档案信息(只能发送咨询时填写的档案,不能发送其他的档案信息,若从快速咨询进入则档案页为空)
- 功能面板点击「文件」选择手机文件系统保存的文件
- 功能面板点击「位置」发送员工当前位置或自选位置信息
- 点击头部「结束咨询」按钮或,弹出结束咨询确认弹窗,确认后跳转到评价医生页
- 导航栏返回 → 图文咨询列表
---
### 3.5 选择体检报告弹窗(select-report-dialog.html
### 3.7 选择体检报告弹窗(select-report-dialog.html
![页面截图](screenshots/select-report-dialog.jpg)
@@ -393,17 +496,16 @@ stateDiagram-v2
**业务规则**
- 已发送的报告按钮变为"已发送"(绿色),不可重复发送
- 报告数据来自健康体检模块
**交互说明**
- 点击「发送」发送报告给医生,按钮变为"已发送"
- 点击「发送」发送将体检报告以卡片形式发送至对话框并自动返回到对话页面
- 点击关闭按钮关闭弹窗
---
### 3.6 结束咨询确认弹窗(end-consult-dialog.html
### 3.7 结束咨询确认弹窗(end-consult-dialog.html
![页面截图](screenshots/end-consult-dialog.jpg)
@@ -419,7 +521,7 @@ stateDiagram-v2
---
### 3.7 图文咨询历史对话(text-chat-history.html
### 3.9 图文咨询历史对话(text-chat-history.html
![页面截图](screenshots/text-chat-history.jpg)
@@ -451,14 +553,13 @@ stateDiagram-v2
**功能说明**
视频咨询预约页面。展示医生信息卡片和预约须知提示。提供日期横向选择器(未来 6 天)和时段网格(上午 6 个 / 下午 6 个),底部确认栏显示已选时段和「立即预约」按钮。
视频咨询预约页面。展示医生信息卡片和预约须知提示。提供日期横向选择器(未来 7 天)和时段网格(时段根据后台设置显示),底部确认栏显示已选时段和「立即预约」按钮。
**业务规则**
- 已约满时段置灰不可选,显示"约满"标记
- 可用时段显示剩余号源数
- 必须同时选择日期和时段才能提交预约
- 视频咨询不收取额外费用,如停诊将短信通知
**交互说明**
@@ -476,16 +577,15 @@ stateDiagram-v2
**功能说明**
视频咨询预约成功结果页。展示成功图标、提示文案(提前 15 分钟推送提醒)、预约信息卡片(医生、医院、科室、类型、时间、费用),以及两个操作按钮。
视频咨询预约成功结果页。展示成功图标、提示文案、预约信息卡片(医生、医院、科室、类型、时间、费用),以及两个操作按钮。
**业务规则**
- 无导航栏返回按钮,仅通过底部按钮离开
- 费用信息以蓝色高亮显示
**交互说明**
- 点击「查看我的咨询」跳转到我的咨询记录
- 点击「查看我的咨询」跳转到我的视频咨询记录列表
- 点击「返回首页」跳转到健康咨询首页
---
@@ -503,8 +603,7 @@ stateDiagram-v2
- 用户提交视频咨询预约后进入待确认状态,等待医生确认接诊
- 医生确认后状态流转为"待开始",并确定最终预约时间
- 用户可在医生确认前取消预约
- "等待医生确认"按钮为置灰禁用状态
- 用户可在咨询未开始前取消预约
**交互说明**
@@ -525,7 +624,6 @@ stateDiagram-v2
**业务规则**
- 预约时间前 15 分钟系统推送提醒
- 用户可在预约时间前取消预约
- "等待预约时间"按钮为置灰禁用状态
@@ -571,14 +669,14 @@ stateDiagram-v2
**业务规则**
- 取消原因由用户取消时填写或系统自动生成
- 取消原因由用户取消时填写或专家填写
- 取消原因区块使用灰色左边框样式
**交互说明**
- 点击「咨询人健康档案」展开/收起档案详情
- 点击「再次预约」跳转到视频咨询预约页
- 导航栏返回 → 我的咨询记录页
- 导航栏返回 → 我的视频咨询记录页
---
@@ -589,12 +687,11 @@ stateDiagram-v2
**功能说明**
视频咨询已拒绝状态的详情页。展示医生信息(红色"已拒绝"标签)、可展开/收起的咨询人健康档案、红色视频咨询 Banner、咨询信息卡片、拒绝原因区块。
视频咨询已拒绝状态的详情页。展示医生信息("已拒绝"标签)、可展开/收起的咨询人健康档案、视频咨询 Banner、咨询信息卡片、拒绝原因区块。
**业务规则**
- 拒绝原因由医生填写
- 拒绝原因区块使用红色左边框样式
**交互说明**
@@ -612,7 +709,7 @@ stateDiagram-v2
**功能说明**
咨询结束后对医生进行评价。展示医生信息和"本次咨询已结束"提示,提供星级评分和文字评价输入。
图文或视频咨询结束后对医生进行评价。展示医生信息和"本次咨询已结束"提示,提供星级评分和文字评价输入。
**业务规则**
@@ -631,10 +728,10 @@ stateDiagram-v2
**表单字段表**
| 字段 | 类型 | 必填 | 规则 |
|------|------|------|------|
| 星级评分 | 星级选择(1~5) | 是 | 默认 5 星 |
| 评价内容 | 多行文本 | 否 | 字数限制 |
| 字段 | 类型 | 必填 | 规则 |
|------|------|------|-----------------|
| 星级评分 | 星级选择(1~5) | 是 | 默认 5 星 |
| 评价内容 | 多行文本 | 否 | 字数限制300 |
| 匿名评价 | 开关 | 否 | 默认关闭,开启后隐藏评价者姓名 |
---
@@ -648,22 +745,22 @@ stateDiagram-v2
展示用户的所有咨询记录。通过一级 Tab 栏切换「咨询专家 / 咨询小助手」两大类型:
- **咨询专家**:内嵌二级 Tab 切换「图文咨询 / 视频咨询」,记录卡片展示医生头像、姓名、科室、状态标签、咨询人、消息摘要、时间
- **咨询小助手**:展示 AI 小助手历史咨询记录列表,卡片展示对话主题、AI 标识、状态标签、消息摘要、时间
- **咨询小助手**:展示咨询小助手历史咨询记录列表,卡片展示对话主题、图像、状态标签、消息摘要、时间
**业务规则**
- 咨询状态及对应样式:`进行中`(蓝色)`已完成`(绿色)`待开始`(橙色)`待确认`(紫色)`已取消`(灰色)`已拒绝`(红色)
- 已完成的图文咨询如已评价则显示"已评价"标记
- 视频咨询 `待确认` 状态显示期望时间,`待开始` 状态显示预约时间,`已完成` 显示通话时长,`已取消` 显示预约时间,`已拒绝` 显示预约时间
- 咨询状态及对应样式:`进行中``已完成``待开始``待确认``已取消``已拒绝``已超时`
- 已完成的咨询如已评价则显示"已评价"标记,未评价则显示"待评价"标记
- 视频咨询 `待确认` `待开始``已取消` `已拒绝` 显示预约时间,`已完成` 显示通话时长,
- 小助手咨询仅有「进行中」和「已完成」两种状态
**交互说明**
- 一级 Tab 栏切换咨询专家/咨询小助手面板
- 咨询专家面板内二级 Tab 切换图文/视频记录列表
- 点击图文咨询记录卡片跳转到咨询详情页
- 点击图文咨询记录卡片已完成的跳转到咨询详情页,进行中的跳转至对话页面
- 点击视频咨询记录卡片根据状态跳转到对应的视频详情页(待确认/待开始/已完成/已取消/已拒绝)
- 点击小助手咨询记录卡片:进行中跳转到 AI 小助手聊天页,已完成跳转到小助手历史对话页
- 点击小助手咨询记录卡片:进行中跳转到小助手聊天页,已完成跳转到小助手历史对话页
- 导航栏返回 → 健康咨询首页
---
@@ -675,47 +772,53 @@ stateDiagram-v2
**功能说明**
展示单条图文咨询记录详情。包含医生信息栏、咨询信息卡片、咨询人健康档案入口、历史对话入口、医生咨询小结。根据咨询是否已评价,显示不同的 UI:
已结束的图文咨询点击咨询记录展示单条图文咨询记录详情。包含医生信息栏、咨询信息卡片、咨询人健康档案入口、历史对话入口、医生咨询小结。根据咨询是否已评价,显示不同的 UI:
- **已评价状态**:显示"我的评价"区域(5 星评价)、医生咨询小结,底部按钮为"再次咨询"(蓝色边框)+ "已评价"(灰色禁用)
- **未评价状态**:隐藏"我的评价"区域,显示医生咨询小结,底部按钮为"再次咨询"(蓝色边框)+ "评价"(蓝色填充)
**业务规则**
- 已完成的咨询显示医生咨询小结
- 已评价的咨询显示 5 星评价,"评价"按钮变为"已评价"禁用状态
- 已评价的咨询显示评价星级"评价"按钮变为"已评价"禁用状态
- 未评价的咨询隐藏评价区域,显示"评价"按钮
**交互说明**
- 点击对话入口卡片跳转到图文咨询历史对话页
- 点击「再次咨询」跳转到图文咨询聊天
- 点击「咨询人健康档案」入口跳转到健康档案详情页
- 点击对话入口卡片跳转到图文咨询历史对话页(只读状态)
- 点击「再次咨询」跳转到本次咨询的专家主
- 点击「咨询人健康档案」入口跳转到健康档案详情页,从快速咨询产生的业务因没有咨询人健康档案信息,自动隐藏该区域
- 点击「评价」按钮(未评价状态)跳转到评价医生页
- 导航栏返回 → 我的咨询记录页
---
## 六、AI 小助手
## 六、咨询小助手
### 6.1 AI 小助手聊天(assistant-chat.html
### 6.1 小助手聊天(assistant-chat.html
![页面截图](screenshots/assistant-chat.jpg)
**功能说明**
AI 智能问诊聊天面。头部展示机器人头像和"健康小助手"。底部输入区含 + 按钮、消息输入框、发送按钮。功能面板提供 4 项操作:图片、拍摄、体检报告、选择档案
点击咨询主页的小助手进入到与小助手的聊天面。头部展示机器人头像和"健康小助手"。底部输入区含 + 按钮、消息输入框、发送按钮。功能面板提供 4 项操作:图片、拍摄、文件、位置
**业务规则**
- AI 回复为系统自动生成,不可人工干预
- 不替代专业医生诊断,仅作初步健康咨询参考
- 结束咨询需通过二次确认弹窗
- 距小组手最后一次回复时间超过7天的且状态是进行中的咨询系统自动置为已完成状态(已结束,待评价)
- 超过7天没有小助手回复的图文咨询系统自动置为超时状态
**交互说明**
- 点击发送按钮发送消息,AI 自动回复
- 点击发送按钮或按 Enter 发送消息,追加到消息列表
- 点击 + 按钮展开/收起功能面板
- 导航栏返回 → 健康咨询首页
- 功能面板点击「图片」选择系统相册中的图片发送至聊天框
- 功能面板点击「拍摄」可拍照或录制视频发送至聊天框
- 功能面板点击「文件」选择手机文件系统保存的文件
- 功能面板点击「位置」发送员工当前位置或自选位置信息
- 点击头部「结束咨询」按钮」,弹出结束咨询确认弹窗,二次确认结束本次咨询跳转至咨询小助手记录列表
- 导航栏返回 → 图文咨询列表
---
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 35 KiB

+2 -2
View File
@@ -95,8 +95,8 @@
<!-- [交互] 排序栏 - 点击切换排序方式 -->
<div class="sort-bar">
<span class="sort-label">排序:</span>
<button class="sort-btn active" onclick="setSortOrder(this)">好评优先</button>
<button class="sort-btn" onclick="setSortOrder(this)">热度优先</button>
<button class="sort-btn active" onclick="setSortOrder(this)">热度优先</button>
<button class="sort-btn" onclick="setSortOrder(this)">好评优先</button>
<button class="sort-btn" onclick="setSortOrder(this)">回复率</button>
</div>
@@ -356,21 +356,6 @@
</div>
</div>
</div>
<!-- 底部三列维度评分 -->
<div class="rating-dimensions">
<div class="dimension-item">
<div class="dimension-score">4.9</div>
<div class="dimension-name">专业性</div>
</div>
<div class="dimension-item">
<div class="dimension-score">4.8</div>
<div class="dimension-name">回复速度</div>
</div>
<div class="dimension-item">
<div class="dimension-score">4.9</div>
<div class="dimension-name">服务态度</div>
</div>
</div>
<!-- 评价总数 -->
<div class="rating-total">共 1,280 条评价</div>
</div>
-2
View File
@@ -133,7 +133,6 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="result-section">
<div class="section-header">
<div class="section-title"><span class="section-icon">👨‍⚕️</span>专家<span class="section-count">(6)</span></div>
<span class="section-more">查看全部 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="#1677ff" stroke-width="1.5"><path d="M4.5 3l3 3-3 3"/></svg></span>
</div>
<!-- [交互] 点击专家卡片跳转到专家主页 -->
<div class="expert-result" onclick="window.location.href='expert-profile.html'">
@@ -166,7 +165,6 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="result-section">
<div class="section-header">
<div class="section-title"><span class="section-icon">🏥</span>医院<span class="section-count">(3)</span></div>
<span class="section-more">查看全部 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="#1677ff" stroke-width="1.5"><path d="M4.5 3l3 3-3 3"/></svg></span>
</div>
<!-- [交互] 点击医院跳转到医院详情 -->
<div class="hospital-result" onclick="window.location.href='hospital-detail.html'">
@@ -1,234 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>选择咨询人弹窗</title>
<style>
/* ===== 全局重置 ===== */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
}
/* ===== 手机壳模拟器 ===== */
.phone-shell {
width: 390px;
height: 844px;
border-radius: 44px;
background: #f5f5f5;
box-shadow: 0 20px 60px rgba(0,0,0,0.3), inset 0 0 0 2px #d0d0d0;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* ===== 状态栏 ===== */
.status-bar {
height: 54px; display: flex; align-items: flex-end; justify-content: space-between;
padding: 0 24px 8px; font-size: 14px; font-weight: 600; color: #333; flex-shrink: 0; background: #fff;
}
.status-bar .time { font-size: 15px; font-weight: 700; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; font-size: 13px; }
.page-content { flex: 1; background: #f5f5f5; }
/* ===== 底部全屏弹窗遮罩 ===== */
.fullsheet-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: flex-end; justify-content: center; z-index: 100; }
/* ===== 全屏弹窗面板 ===== */
.fullsheet { background: #fff; border-radius: 24px 24px 0 0; width: 100%; max-height: 82%; display: flex; flex-direction: column; }
.fullsheet-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px 12px; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; }
.fullsheet-header h3 { font-size: 16px; font-weight: 700; color: #333; }
.fullsheet-header .close-btn { font-size: 22px; color: #999; cursor: pointer; background: none; border: none; line-height: 1; }
.fullsheet-body { flex: 1; overflow-y: auto; padding: 12px 16px; }
/* ===== 咨询人卡片 ===== */
.person-card { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 14px; border: 1.5px solid #e8e8e8; margin-bottom: 10px; cursor: pointer; }
.person-card:active, .person-card.selected { border-color: #1677ff; background: #e8f4ff; }
.person-avatar { font-size: 28px; flex-shrink: 0; }
.person-info { flex: 1; }
.person-name { font-size: 15px; font-weight: 600; color: #333; }
.person-detail { font-size: 12px; color: #999; margin-top: 2px; }
.person-tag { font-size: 11px; background: #f0f5ff; color: #1677ff; padding: 2px 8px; border-radius: 10px; flex-shrink: 0; }
/* ===== 新增咨询人按钮 ===== */
.add-person-btn { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; padding: 13px; border-radius: 14px; border: 1.5px dashed #d0d0d0; background: #fafafa; font-size: 14px; color: #666; cursor: pointer; }
/* ===== 新增表单视图 ===== */
.form-view { display: none; }
.form-view.active { display: block; }
.list-view.hidden { display: none; }
.form-back { font-size: 14px; color: #1677ff; cursor: pointer; background: none; border: none; margin-bottom: 14px; }
.form-group { margin-bottom: 14px; }
.form-label { font-size: 13px; color: #666; margin-bottom: 6px; display: block; }
.form-input { width: 100%; padding: 10px 12px; border: 1.5px solid #e0e0e0; border-radius: 10px; font-size: 14px; outline: none; }
.form-input:focus { border-color: #1677ff; }
.form-section { font-size: 14px; font-weight: 600; color: #333; margin: 18px 0 10px; padding-bottom: 6px; border-bottom: 1px solid #f0f0f0; }
/* ===== 性别/选项按钮组 ===== */
.gender-group { display: flex; gap: 8px; margin-bottom: 14px; }
.gender-btn { flex: 1; padding: 9px; border: 1.5px solid #e0e0e0; border-radius: 10px; font-size: 14px; background: #fff; cursor: pointer; text-align: center; }
.gender-btn.active { border-color: #1677ff; background: #e8f4ff; color: #1677ff; font-weight: 600; }
.option-group { display: flex; gap: 8px; flex-wrap: wrap; }
.option-btn { padding: 7px 14px; border: 1.5px solid #e0e0e0; border-radius: 10px; font-size: 13px; background: #fff; cursor: pointer; }
.option-btn.active { border-color: #1677ff; background: #e8f4ff; color: #1677ff; font-weight: 600; }
/* ===== 保存按钮 ===== */
.save-btn { width: 100%; padding: 12px; border-radius: 24px; font-size: 15px; font-weight: 600; background: #1677ff; color: #fff; border: none; cursor: pointer; margin-top: 16px; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<span class="icons"><span>&#9679;&#9679;&#9679;&#9679;</span><span>WiFi</span><span>&#128267;</span></span>
</div>
<div class="page-content"></div>
<!-- [交互] 选择咨询人弹窗 - 默认打开状态 -->
<div class="fullsheet-overlay" id="sheetOverlay">
<div class="fullsheet">
<div class="fullsheet-header">
<h3 id="sheetTitle">选择咨询人</h3>
<button class="close-btn" onclick="closeSheet()">&times;</button>
</div>
<div class="fullsheet-body">
<!-- 列表视图 -->
<div class="list-view" id="listView">
<div class="person-card selected" onclick="selectPerson(this)">
<span class="person-avatar">👤</span>
<div class="person-info"><div class="person-name">本人</div><div class="person-detail">28岁 · 男</div></div>
<span class="person-tag">本人</span>
</div>
<div class="person-card" onclick="selectPerson(this)">
<span class="person-avatar">👴</span>
<div class="person-info"><div class="person-name">张三</div><div class="person-detail">58岁 · 男</div></div>
<span class="person-tag">父亲</span>
</div>
<div class="person-card" onclick="selectPerson(this)">
<span class="person-avatar">👩</span>
<div class="person-info"><div class="person-name">李梅</div><div class="person-detail">55岁 · 女</div></div>
<span class="person-tag">母亲</span>
</div>
<div class="person-card" onclick="selectPerson(this)">
<span class="person-avatar">🧒</span>
<div class="person-info"><div class="person-name">张小宇</div><div class="person-detail">8岁 · 男</div></div>
<span class="person-tag">儿子</span>
</div>
<!-- 新增咨询人入口 -->
<div class="add-person-btn" onclick="showAddForm()">+ 新增咨询人</div>
<div id="newPersons"></div>
</div>
<!-- 新增咨询人表单视图 -->
<div class="form-view" id="formView">
<button class="form-back" onclick="backToList()">← 返回</button>
<div class="form-group"><label class="form-label">姓名</label><input class="form-input" id="fname" placeholder="请输入姓名"></div>
<div class="form-group">
<label class="form-label">性别</label>
<div class="gender-group" data-field="gender">
<div class="gender-btn active" onclick="toggleBtn(this)"></div>
<div class="gender-btn" onclick="toggleBtn(this)"></div>
</div>
</div>
<div class="form-group"><label class="form-label">出生日期</label><input class="form-input" type="date" id="fbirthdate"></div>
<div class="form-group"><label class="form-label">与本人关系</label><input class="form-input" id="frelation" placeholder="如:配偶、朋友等"></div>
<div class="form-section">生活习惯</div>
<div class="form-group">
<label class="form-label">是否吸烟</label>
<div class="option-group" data-field="smoke"><div class="option-btn active" onclick="toggleBtn(this)"></div><div class="option-btn" onclick="toggleBtn(this)">偶尔</div><div class="option-btn" onclick="toggleBtn(this)"></div></div>
</div>
<div class="form-group">
<label class="form-label">是否饮酒</label>
<div class="option-group" data-field="drink"><div class="option-btn active" onclick="toggleBtn(this)"></div><div class="option-btn" onclick="toggleBtn(this)">偶尔</div><div class="option-btn" onclick="toggleBtn(this)"></div></div>
</div>
<div class="form-group">
<label class="form-label">饮食喜好</label>
<div class="option-group" data-field="diet"><div class="option-btn active" onclick="toggleBtn(this)">清淡</div><div class="option-btn" onclick="toggleBtn(this)">均衡</div><div class="option-btn" onclick="toggleBtn(this)">偏油腻</div><div class="option-btn" onclick="toggleBtn(this)">偏辛辣</div></div>
</div>
<div class="form-group">
<label class="form-label">睡眠质量</label>
<div class="option-group" data-field="sleep"><div class="option-btn active" onclick="toggleBtn(this)">良好</div><div class="option-btn" onclick="toggleBtn(this)">一般</div><div class="option-btn" onclick="toggleBtn(this)">较差</div></div>
</div>
<div class="form-section">身体情况</div>
<div class="form-group" style="display:flex;gap:8px;">
<div style="flex:1"><label class="form-label">身高(cm)</label><input class="form-input" id="fheight" placeholder="170"></div>
<div style="flex:1"><label class="form-label">体重(kg)</label><input class="form-input" id="fweight" placeholder="65"></div>
</div>
<div class="form-group"><label class="form-label">过敏史</label><input class="form-input" id="fallergy" placeholder="如:青霉素过敏"></div>
<div class="form-group"><label class="form-label">遗传史</label><input class="form-input" id="fgenetic" placeholder="如:高血压家族史"></div>
<div class="form-group"><label class="form-label">既往史</label><input class="form-input" id="fpast" placeholder="如:2020年阑尾炎手术"></div>
<div class="form-group"><label class="form-label">其他疾病</label><input class="form-input" id="fother" placeholder="如:慢性胃炎"></div>
<div class="form-label">温馨提示:咨询人信息用于平台审核,不会外传。请输入正确的咨询人信息</div>
<button class="save-btn" onclick="savePerson()">保存</button>
</div>
</div>
</div>
</div>
</div>
<script>
/* ===== 交互逻辑 ===== */
/** 关闭弹窗 - 返回上一页 */
function closeSheet() {
history.back();
}
/** 选中咨询人卡片 - 切换高亮状态 */
function selectPerson(card) {
document.querySelectorAll('.person-card').forEach(function(c) { c.classList.remove('selected'); });
card.classList.add('selected');
}
/** 显示新增表单视图 */
function showAddForm() {
document.getElementById('listView').classList.add('hidden');
document.getElementById('formView').classList.add('active');
document.getElementById('sheetTitle').textContent = '新增咨询人';
}
/** 返回列表视图 */
function backToList() {
document.getElementById('formView').classList.remove('active');
document.getElementById('listView').classList.remove('hidden');
document.getElementById('sheetTitle').textContent = '选择咨询人';
}
/** 按钮组切换 - 同组内单选 */
function toggleBtn(btn) {
var siblings = btn.parentElement.children;
for (var i = 0; i < siblings.length; i++) { siblings[i].classList.remove('active'); }
btn.classList.add('active');
}
/** 保存新咨询人 - 添加卡片并返回列表 */
function savePerson() {
var name = document.getElementById('fname').value.trim();
if (!name) { alert('请输入姓名'); return; }
var gender = document.querySelector('[data-field="gender"] .active').textContent;
var relation = document.getElementById('frelation').value.trim() || '其他';
var emoji = gender === '女' ? '👩' : '👤';
// 创建新卡片并插入列表
var card = document.createElement('div');
card.className = 'person-card';
card.setAttribute('onclick', 'selectPerson(this)');
card.innerHTML = '<span class="person-avatar">' + emoji + '</span><div class="person-info"><div class="person-name">' + name + '</div><div class="person-detail">' + gender + '</div></div><span class="person-tag">' + relation + '</span>';
document.getElementById('newPersons').appendChild(card);
// 重置表单并返回列表
document.getElementById('fname').value = '';
document.getElementById('frelation').value = '';
backToList();
}
</script>
</body>
</html>
@@ -166,8 +166,6 @@
</div>
<!-- 温馨提示卡片 -->
<div class="notice-card">📌 温馨提示:请详细描述您的症状,医生将尽快回复。如遇紧急情况请立即就医。</div>
<!-- 聊天消息区域(可滚动) -->
<div class="chat-messages" id="chatMessages">
<!-- 医生消息 -->
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../playwright/cli.js" "$@"
else
exec node "$basedir/../playwright/cli.js" "$@"
fi
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../playwright-core/cli.js" "$@"
else
exec node "$basedir/../playwright-core/cli.js" "$@"
fi
+17
View File
@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\playwright-core\cli.js" %*
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../playwright-core/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../playwright-core/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../playwright-core/cli.js" $args
} else {
& "node$exe" "$basedir/../playwright-core/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
+17
View File
@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\playwright\cli.js" %*
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../playwright/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../playwright/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../playwright/cli.js" $args
} else {
& "node$exe" "$basedir/../playwright/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret
+37
View File
@@ -0,0 +1,37 @@
{
"name": "platform-prototype",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/playwright": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Portions Copyright (c) Microsoft Corporation.
Portions Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+5
View File
@@ -0,0 +1,5 @@
Playwright
Copyright (c) Microsoft Corporation
This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer),
available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE).
+3
View File
@@ -0,0 +1,3 @@
# playwright-core
This package contains the no-browser flavor of [Playwright](http://github.com/microsoft/playwright).
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
# check if running on Windows Server
if ($osInfo.ProductType -eq 3) {
Install-WindowsFeature Server-Media-Foundation
}
+33
View File
@@ -0,0 +1,33 @@
$ErrorActionPreference = 'Stop'
# This script sets up a WSL distribution that will be used to run WebKit.
$Distribution = "playwright"
$Username = "pwuser"
$distributions = (wsl --list --quiet) -split "\r?\n"
if ($distributions -contains $Distribution) {
Write-Host "WSL distribution '$Distribution' already exists. Skipping installation."
} else {
Write-Host "Installing new WSL distribution '$Distribution'..."
$VhdSize = "10GB"
wsl --install -d Ubuntu-24.04 --name $Distribution --no-launch --vhd-size $VhdSize
wsl -d $Distribution -u root adduser --gecos GECOS --disabled-password $Username
}
$pwshDirname = (Resolve-Path -Path $PSScriptRoot).Path;
$playwrightCoreRoot = Resolve-Path (Join-Path $pwshDirname "..")
$initScript = @"
if [ ! -f "/home/$Username/node/bin/node" ]; then
mkdir -p /home/$Username/node
curl -fsSL https://nodejs.org/dist/v22.17.0/node-v22.17.0-linux-x64.tar.xz -o /home/$Username/node/node-v22.17.0-linux-x64.tar.xz
tar -xJf /home/$Username/node/node-v22.17.0-linux-x64.tar.xz -C /home/$Username/node --strip-components=1
sudo -u $Username echo 'export PATH=/home/$Username/node/bin:\`$PATH' >> /home/$Username/.profile
fi
/home/$Username/node/bin/node cli.js install-deps webkit
sudo -u $Username PLAYWRIGHT_SKIP_BROWSER_GC=1 /home/$Username/node/bin/node cli.js install webkit
"@ -replace "\r\n", "`n"
wsl -d $Distribution --cd $playwrightCoreRoot -u root -- bash -c "$initScript"
Write-Host "Done!"
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -e
set -x
if [[ $(arch) == "aarch64" ]]; then
echo "ERROR: not supported on Linux Arm64"
exit 1
fi
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
if [[ ! -f "/etc/os-release" ]]; then
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
exit 1
fi
ID=$(bash -c 'source /etc/os-release && echo $ID')
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
exit 1
fi
fi
# 1. make sure to remove old beta if any.
if dpkg --get-selections | grep -q "^google-chrome-beta[[:space:]]*install$" >/dev/null; then
apt-get remove -y google-chrome-beta
fi
# 2. Update apt lists (needed to install curl and chrome dependencies)
apt-get update
# 3. Install curl to download chrome
if ! command -v curl >/dev/null; then
apt-get install -y curl
fi
# 4. download chrome beta from dl.google.com and install it.
cd /tmp
curl -O https://dl.google.com/linux/direct/google-chrome-beta_current_amd64.deb
apt-get install -y ./google-chrome-beta_current_amd64.deb
rm -rf ./google-chrome-beta_current_amd64.deb
cd -
google-chrome-beta --version
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
set -x
rm -rf "/Applications/Google Chrome Beta.app"
cd /tmp
curl --retry 3 -o ./googlechromebeta.dmg https://dl.google.com/chrome/mac/universal/beta/googlechromebeta.dmg
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechromebeta.dmg ./googlechromebeta.dmg
cp -pR "/Volumes/googlechromebeta.dmg/Google Chrome Beta.app" /Applications
hdiutil detach /Volumes/googlechromebeta.dmg
rm -rf /tmp/googlechromebeta.dmg
/Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta --version
+24
View File
@@ -0,0 +1,24 @@
$ErrorActionPreference = 'Stop'
$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromebetastandaloneenterprise64.msi'
Write-Host "Downloading Google Chrome Beta"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\google-chrome-beta.msi"
$wc.Downloadfile($url, $msiInstaller)
Write-Host "Installing Google Chrome Beta"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller
$suffix = "\\Google\\Chrome Beta\\Application\\chrome.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
Write-Host "ERROR: Failed to install Google Chrome Beta."
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
exit 1
}
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -e
set -x
if [[ $(arch) == "aarch64" ]]; then
echo "ERROR: not supported on Linux Arm64"
exit 1
fi
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
if [[ ! -f "/etc/os-release" ]]; then
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
exit 1
fi
ID=$(bash -c 'source /etc/os-release && echo $ID')
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
exit 1
fi
fi
# 1. make sure to remove old stable if any.
if dpkg --get-selections | grep -q "^google-chrome[[:space:]]*install$" >/dev/null; then
apt-get remove -y google-chrome
fi
# 2. Update apt lists (needed to install curl and chrome dependencies)
apt-get update
# 3. Install curl to download chrome
if ! command -v curl >/dev/null; then
apt-get install -y curl
fi
# 4. download chrome stable from dl.google.com and install it.
cd /tmp
curl -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt-get install -y ./google-chrome-stable_current_amd64.deb
rm -rf ./google-chrome-stable_current_amd64.deb
cd -
google-chrome --version
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
set -x
rm -rf "/Applications/Google Chrome.app"
cd /tmp
curl --retry 3 -o ./googlechrome.dmg https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechrome.dmg ./googlechrome.dmg
cp -pR "/Volumes/googlechrome.dmg/Google Chrome.app" /Applications
hdiutil detach /Volumes/googlechrome.dmg
rm -rf /tmp/googlechrome.dmg
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
+24
View File
@@ -0,0 +1,24 @@
$ErrorActionPreference = 'Stop'
$url = 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi'
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\google-chrome.msi"
Write-Host "Downloading Google Chrome"
$wc.Downloadfile($url, $msiInstaller)
Write-Host "Installing Google Chrome"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller
$suffix = "\\Google\\Chrome\\Application\\chrome.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
Write-Host "ERROR: Failed to install Google Chrome."
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
exit 1
}
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -e
set -x
if [[ $(arch) == "aarch64" ]]; then
echo "ERROR: not supported on Linux Arm64"
exit 1
fi
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
if [[ ! -f "/etc/os-release" ]]; then
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
exit 1
fi
ID=$(bash -c 'source /etc/os-release && echo $ID')
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
exit 1
fi
fi
# 1. make sure to remove old beta if any.
if dpkg --get-selections | grep -q "^microsoft-edge-beta[[:space:]]*install$" >/dev/null; then
apt-get remove -y microsoft-edge-beta
fi
# 2. Install curl to download Microsoft gpg key
if ! command -v curl >/dev/null; then
apt-get update
apt-get install -y curl
fi
# GnuPG is not preinstalled in slim images
if ! command -v gpg >/dev/null; then
apt-get update
apt-get install -y gpg
fi
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list'
rm /tmp/microsoft.gpg
apt-get update && apt-get install -y microsoft-edge-beta
microsoft-edge-beta --version
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
set -x
cd /tmp
curl --retry 3 -o ./msedge_beta.pkg "$1"
# Note: there's no way to uninstall previously installed MSEdge.
# However, running PKG again seems to update installation.
sudo installer -pkg /tmp/msedge_beta.pkg -target /
rm -rf /tmp/msedge_beta.pkg
/Applications/Microsoft\ Edge\ Beta.app/Contents/MacOS/Microsoft\ Edge\ Beta --version
+23
View File
@@ -0,0 +1,23 @@
$ErrorActionPreference = 'Stop'
$url = $args[0]
Write-Host "Downloading Microsoft Edge Beta"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\microsoft-edge-beta.msi"
$wc.Downloadfile($url, $msiInstaller)
Write-Host "Installing Microsoft Edge Beta"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller
$suffix = "\\Microsoft\\Edge Beta\\Application\\msedge.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
Write-Host "ERROR: Failed to install Microsoft Edge Beta."
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
exit 1
}
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -e
set -x
if [[ $(arch) == "aarch64" ]]; then
echo "ERROR: not supported on Linux Arm64"
exit 1
fi
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
if [[ ! -f "/etc/os-release" ]]; then
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
exit 1
fi
ID=$(bash -c 'source /etc/os-release && echo $ID')
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
exit 1
fi
fi
# 1. make sure to remove old dev if any.
if dpkg --get-selections | grep -q "^microsoft-edge-dev[[:space:]]*install$" >/dev/null; then
apt-get remove -y microsoft-edge-dev
fi
# 2. Install curl to download Microsoft gpg key
if ! command -v curl >/dev/null; then
apt-get update
apt-get install -y curl
fi
# GnuPG is not preinstalled in slim images
if ! command -v gpg >/dev/null; then
apt-get update
apt-get install -y gpg
fi
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list'
rm /tmp/microsoft.gpg
apt-get update && apt-get install -y microsoft-edge-dev
microsoft-edge-dev --version
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
set -x
cd /tmp
curl --retry 3 -o ./msedge_dev.pkg "$1"
# Note: there's no way to uninstall previously installed MSEdge.
# However, running PKG again seems to update installation.
sudo installer -pkg /tmp/msedge_dev.pkg -target /
rm -rf /tmp/msedge_dev.pkg
/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev --version
+23
View File
@@ -0,0 +1,23 @@
$ErrorActionPreference = 'Stop'
$url = $args[0]
Write-Host "Downloading Microsoft Edge Dev"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\microsoft-edge-dev.msi"
$wc.Downloadfile($url, $msiInstaller)
Write-Host "Installing Microsoft Edge Dev"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller
$suffix = "\\Microsoft\\Edge Dev\\Application\\msedge.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
Write-Host "ERROR: Failed to install Microsoft Edge Dev."
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
exit 1
}
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -e
set -x
if [[ $(arch) == "aarch64" ]]; then
echo "ERROR: not supported on Linux Arm64"
exit 1
fi
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
if [[ ! -f "/etc/os-release" ]]; then
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
exit 1
fi
ID=$(bash -c 'source /etc/os-release && echo $ID')
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
exit 1
fi
fi
# 1. make sure to remove old stable if any.
if dpkg --get-selections | grep -q "^microsoft-edge-stable[[:space:]]*install$" >/dev/null; then
apt-get remove -y microsoft-edge-stable
fi
# 2. Install curl to download Microsoft gpg key
if ! command -v curl >/dev/null; then
apt-get update
apt-get install -y curl
fi
# GnuPG is not preinstalled in slim images
if ! command -v gpg >/dev/null; then
apt-get update
apt-get install -y gpg
fi
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-stable.list'
rm /tmp/microsoft.gpg
apt-get update && apt-get install -y microsoft-edge-stable
microsoft-edge-stable --version
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
set -x
cd /tmp
curl --retry 3 -o ./msedge_stable.pkg "$1"
# Note: there's no way to uninstall previously installed MSEdge.
# However, running PKG again seems to update installation.
sudo installer -pkg /tmp/msedge_stable.pkg -target /
rm -rf /tmp/msedge_stable.pkg
/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --version
+24
View File
@@ -0,0 +1,24 @@
$ErrorActionPreference = 'Stop'
$url = $args[0]
Write-Host "Downloading Microsoft Edge"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\microsoft-edge-stable.msi"
$wc.Downloadfile($url, $msiInstaller)
Write-Host "Installing Microsoft Edge"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller
$suffix = "\\Microsoft\\Edge\\Application\\msedge.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
Write-Host "ERROR: Failed to install Microsoft Edge."
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
exit 1
}
+81
View File
@@ -0,0 +1,81 @@
{
"comment": "Do not edit this file, use utils/roll_browser.js",
"browsers": [
{
"name": "chromium",
"revision": "1217",
"installByDefault": true,
"browserVersion": "147.0.7727.15",
"title": "Chrome for Testing"
},
{
"name": "chromium-headless-shell",
"revision": "1217",
"installByDefault": true,
"browserVersion": "147.0.7727.15",
"title": "Chrome Headless Shell"
},
{
"name": "chromium-tip-of-tree",
"revision": "1417",
"installByDefault": false,
"browserVersion": "148.0.7755.0",
"title": "Chrome Canary for Testing"
},
{
"name": "chromium-tip-of-tree-headless-shell",
"revision": "1417",
"installByDefault": false,
"browserVersion": "148.0.7755.0",
"title": "Chrome Canary Headless Shell"
},
{
"name": "firefox",
"revision": "1511",
"installByDefault": true,
"browserVersion": "148.0.2",
"title": "Firefox"
},
{
"name": "firefox-beta",
"revision": "1505",
"installByDefault": false,
"browserVersion": "148.0b9",
"title": "Firefox Beta"
},
{
"name": "webkit",
"revision": "2272",
"installByDefault": true,
"revisionOverrides": {
"mac14": "2251",
"mac14-arm64": "2251",
"debian11-x64": "2105",
"debian11-arm64": "2105",
"ubuntu20.04-x64": "2092",
"ubuntu20.04-arm64": "2092"
},
"browserVersion": "26.4",
"title": "WebKit"
},
{
"name": "ffmpeg",
"revision": "1011",
"installByDefault": true,
"revisionOverrides": {
"mac12": "1010",
"mac12-arm64": "1010"
}
},
{
"name": "winldd",
"revision": "1007",
"installByDefault": false
},
{
"name": "android",
"revision": "1001",
"installByDefault": false
}
]
}
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { program } = require('./lib/cli/programWithTestStub');
program.parse(process.argv);
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './types/types';
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const minimumMajorNodeVersion = 18;
const currentNodeVersion = process.versions.node;
const semver = currentNodeVersion.split('.');
const [major] = [+semver[0]];
if (major < minimumMajorNodeVersion) {
console.error(
'You are running Node.js ' +
currentNodeVersion +
'.\n' +
`Playwright requires Node.js ${minimumMajorNodeVersion} or higher. \n` +
'Please update your version of Node.js.'
);
process.exit(1);
}
module.exports = require('./lib/inprocess');
+28
View File
@@ -0,0 +1,28 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import playwright from './index.js';
export const chromium = playwright.chromium;
export const firefox = playwright.firefox;
export const webkit = playwright.webkit;
export const selectors = playwright.selectors;
export const devices = playwright.devices;
export const errors = playwright.errors;
export const request = playwright.request;
export const _electron = playwright._electron;
export const _android = playwright._android;
export default playwright;
+65
View File
@@ -0,0 +1,65 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var androidServerImpl_exports = {};
__export(androidServerImpl_exports, {
AndroidServerLauncherImpl: () => AndroidServerLauncherImpl
});
module.exports = __toCommonJS(androidServerImpl_exports);
var import_playwrightServer = require("./remote/playwrightServer");
var import_playwright = require("./server/playwright");
var import_crypto = require("./server/utils/crypto");
var import_utilsBundle = require("./utilsBundle");
var import_progress = require("./server/progress");
class AndroidServerLauncherImpl {
async launchServer(options = {}) {
const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
const controller = new import_progress.ProgressController();
let devices = await controller.run((progress) => playwright.android.devices(progress, {
host: options.adbHost,
port: options.adbPort,
omitDriverInstall: options.omitDriverInstall
}));
if (devices.length === 0)
throw new Error("No devices found");
if (options.deviceSerialNumber) {
devices = devices.filter((d) => d.serial === options.deviceSerialNumber);
if (devices.length === 0)
throw new Error(`No device with serial number '${options.deviceSerialNumber}' was found`);
}
if (devices.length > 1)
throw new Error(`More than one device found. Please specify deviceSerialNumber`);
const device = devices[0];
const path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
const server = new import_playwrightServer.PlaywrightServer({ mode: "launchServer", path, maxConnections: 1, preLaunchedAndroidDevice: device });
const wsEndpoint = await server.listen(options.port, options.host);
const browserServer = new import_utilsBundle.ws.EventEmitter();
browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => device.close();
browserServer.kill = () => device.close();
device.on("close", () => {
server.close();
browserServer.emit("close");
});
return browserServer;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AndroidServerLauncherImpl
});
+77
View File
@@ -0,0 +1,77 @@
"use strict";
if (process.env.PW_INSTRUMENT_MODULES) {
const Module = require("module");
const originalLoad = Module._load;
const root = { name: "<root>", selfMs: 0, totalMs: 0, childrenMs: 0, children: [] };
let current = root;
const stack = [];
Module._load = function(request, _parent, _isMain) {
const node = { name: request, selfMs: 0, totalMs: 0, childrenMs: 0, children: [] };
current.children.push(node);
stack.push(current);
current = node;
const start = performance.now();
let result;
try {
result = originalLoad.apply(this, arguments);
} catch (e) {
current = stack.pop();
current.children.pop();
throw e;
}
const duration = performance.now() - start;
node.totalMs = duration;
node.selfMs = Math.max(0, duration - node.childrenMs);
current = stack.pop();
current.childrenMs += duration;
return result;
};
process.on("exit", () => {
function printTree(node, prefix, isLast, lines2, depth) {
if (node.totalMs < 1 && depth > 0)
return;
const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
const time = `${node.totalMs.toFixed(1).padStart(8)}ms`;
const self = node.children.length ? ` (self: ${node.selfMs.toFixed(1)}ms)` : "";
lines2.push(`${time} ${prefix}${connector}${node.name}${self}`);
const childPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 ");
const sorted2 = node.children.slice().sort((a, b) => b.totalMs - a.totalMs);
for (let i = 0; i < sorted2.length; i++)
printTree(sorted2[i], childPrefix, i === sorted2.length - 1, lines2, depth + 1);
}
let totalModules = 0;
function count(n) {
totalModules++;
n.children.forEach(count);
}
root.children.forEach(count);
const lines = [];
const sorted = root.children.slice().sort((a, b) => b.totalMs - a.totalMs);
for (let i = 0; i < sorted.length; i++)
printTree(sorted[i], "", i === sorted.length - 1, lines, 0);
const totalMs = root.children.reduce((s, c) => s + c.totalMs, 0);
process.stderr.write(`
--- Module load tree: ${totalModules} modules, ${totalMs.toFixed(0)}ms total ---
` + lines.join("\n") + "\n");
const flat = /* @__PURE__ */ new Map();
function gather(n) {
const existing = flat.get(n.name);
if (existing) {
existing.selfMs += n.selfMs;
existing.totalMs += n.totalMs;
existing.count++;
} else {
flat.set(n.name, { selfMs: n.selfMs, totalMs: n.totalMs, count: 1 });
}
n.children.forEach(gather);
}
root.children.forEach(gather);
const top50 = [...flat.entries()].sort((a, b) => b[1].selfMs - a[1].selfMs).slice(0, 50);
const flatLines = top50.map(
([mod, { selfMs, totalMs: totalMs2, count: count2 }]) => `${selfMs.toFixed(1).padStart(8)}ms self ${totalMs2.toFixed(1).padStart(8)}ms total (x${String(count2).padStart(3)}) ${mod}`
);
process.stderr.write(`
--- Top 50 modules by self time ---
` + flatLines.join("\n") + "\n");
});
}
+120
View File
@@ -0,0 +1,120 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserServerImpl_exports = {};
__export(browserServerImpl_exports, {
BrowserServerLauncherImpl: () => BrowserServerLauncherImpl
});
module.exports = __toCommonJS(browserServerImpl_exports);
var import_playwrightServer = require("./remote/playwrightServer");
var import_helper = require("./server/helper");
var import_playwright = require("./server/playwright");
var import_crypto = require("./server/utils/crypto");
var import_debug = require("./server/utils/debug");
var import_stackTrace = require("./utils/isomorphic/stackTrace");
var import_time = require("./utils/isomorphic/time");
var import_utilsBundle = require("./utilsBundle");
var validatorPrimitives = __toESM(require("./protocol/validatorPrimitives"));
var import_progress = require("./server/progress");
class BrowserServerLauncherImpl {
constructor(browserName) {
this._browserName = browserName;
}
async launchServer(options = {}) {
const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
const metadata = { id: "", startTime: 0, endTime: 0, type: "Internal", method: "", params: {}, log: [], internal: true };
const validatorContext = {
tChannelImpl: (names, arg, path2) => {
throw new validatorPrimitives.ValidationError(`${path2}: channels are not expected in launchServer`);
},
binary: "buffer",
isUnderTest: import_debug.isUnderTest
};
let launchOptions = {
...options,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? envObjectToArray(options.env) : void 0,
timeout: options.timeout ?? import_time.DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT
};
let browser;
try {
const controller = new import_progress.ProgressController(metadata);
browser = await controller.run(async (progress) => {
if (options._userDataDir !== void 0) {
const validator = validatorPrimitives.scheme["BrowserTypeLaunchPersistentContextParams"];
launchOptions = validator({ ...launchOptions, userDataDir: options._userDataDir }, "", validatorContext);
const context = await playwright[this._browserName].launchPersistentContext(progress, options._userDataDir, launchOptions);
return context._browser;
} else {
const validator = validatorPrimitives.scheme["BrowserTypeLaunchParams"];
launchOptions = validator(launchOptions, "", validatorContext);
return await playwright[this._browserName].launch(progress, launchOptions, toProtocolLogger(options.logger));
}
});
} catch (e) {
const log = import_helper.helper.formatBrowserLogs(metadata.log);
(0, import_stackTrace.rewriteErrorMessage)(e, `${e.message} Failed to launch browser.${log}`);
throw e;
}
const path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
const server = new import_playwrightServer.PlaywrightServer({ mode: options._sharedBrowser ? "launchServerShared" : "launchServer", path, maxConnections: Infinity, preLaunchedBrowser: browser });
const wsEndpoint = await server.listen(options.port, options.host);
const browserServer = new import_utilsBundle.ws.EventEmitter();
browserServer.process = () => browser.options.browserProcess.process;
browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => browser.options.browserProcess.close();
browserServer[Symbol.asyncDispose] = browserServer.close;
browserServer.kill = () => browser.options.browserProcess.kill();
browserServer._disconnectForTest = () => server.close();
browserServer._userDataDirForTest = browser._userDataDirForTest;
browser.options.browserProcess.onclose = (exitCode, signal) => {
server.close();
browserServer.emit("close", exitCode, signal);
};
return browserServer;
}
}
function toProtocolLogger(logger) {
return logger ? (direction, message) => {
if (logger.isEnabled("protocol", "verbose"))
logger.log("protocol", "verbose", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(message), [], {});
} : void 0;
}
function envObjectToArray(env) {
const result = [];
for (const name in env) {
if (!Object.is(env[name], void 0))
result.push({ name, value: String(env[name]) });
}
return result;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserServerLauncherImpl
});
+308
View File
@@ -0,0 +1,308 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserActions_exports = {};
__export(browserActions_exports, {
codegen: () => codegen,
open: () => open,
pdf: () => pdf,
screenshot: () => screenshot
});
module.exports = __toCommonJS(browserActions_exports);
var import_fs = __toESM(require("fs"));
var import_os = __toESM(require("os"));
var import_path = __toESM(require("path"));
var playwright = __toESM(require("../.."));
var import_utils = require("../utils");
var import_utilsBundle = require("../utilsBundle");
async function launchContext(options, extraOptions) {
validateOptions(options);
const browserType = lookupBrowserType(options);
const launchOptions = extraOptions;
if (options.channel)
launchOptions.channel = options.channel;
launchOptions.handleSIGINT = false;
const contextOptions = (
// Copy the device descriptor since we have to compare and modify the options.
options.device ? { ...playwright.devices[options.device] } : {}
);
if (!extraOptions.headless)
contextOptions.deviceScaleFactor = import_os.default.platform() === "darwin" ? 2 : 1;
if (browserType.name() === "webkit" && process.platform === "linux") {
delete contextOptions.hasTouch;
delete contextOptions.isMobile;
}
if (contextOptions.isMobile && browserType.name() === "firefox")
contextOptions.isMobile = void 0;
if (options.blockServiceWorkers)
contextOptions.serviceWorkers = "block";
if (options.proxyServer) {
launchOptions.proxy = {
server: options.proxyServer
};
if (options.proxyBypass)
launchOptions.proxy.bypass = options.proxyBypass;
}
if (options.viewportSize) {
try {
const [width, height] = options.viewportSize.split(",").map((n) => +n);
if (isNaN(width) || isNaN(height))
throw new Error("bad values");
contextOptions.viewport = { width, height };
} catch (e) {
throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"');
}
}
if (options.geolocation) {
try {
const [latitude, longitude] = options.geolocation.split(",").map((n) => parseFloat(n.trim()));
contextOptions.geolocation = {
latitude,
longitude
};
} catch (e) {
throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"');
}
contextOptions.permissions = ["geolocation"];
}
if (options.userAgent)
contextOptions.userAgent = options.userAgent;
if (options.lang)
contextOptions.locale = options.lang;
if (options.colorScheme)
contextOptions.colorScheme = options.colorScheme;
if (options.timezone)
contextOptions.timezoneId = options.timezone;
if (options.loadStorage)
contextOptions.storageState = options.loadStorage;
if (options.ignoreHttpsErrors)
contextOptions.ignoreHTTPSErrors = true;
if (options.saveHar) {
contextOptions.recordHar = { path: import_path.default.resolve(process.cwd(), options.saveHar), mode: "minimal" };
if (options.saveHarGlob)
contextOptions.recordHar.urlFilter = options.saveHarGlob;
contextOptions.serviceWorkers = "block";
}
let browser;
let context;
if (options.userDataDir) {
context = await browserType.launchPersistentContext(options.userDataDir, { ...launchOptions, ...contextOptions });
browser = context.browser();
} else {
browser = await browserType.launch(launchOptions);
context = await browser.newContext(contextOptions);
}
let closingBrowser = false;
async function closeBrowser() {
if (closingBrowser)
return;
closingBrowser = true;
if (options.saveStorage)
await context.storageState({ path: options.saveStorage }).catch((e) => null);
if (options.saveHar)
await context.close();
await browser.close();
}
context.on("page", (page) => {
page.on("dialog", () => {
});
page.on("close", () => {
const hasPage = browser.contexts().some((context2) => context2.pages().length > 0);
if (hasPage)
return;
closeBrowser().catch(() => {
});
});
});
process.on("SIGINT", async () => {
await closeBrowser();
(0, import_utils.gracefullyProcessExitDoNotHang)(130);
});
const timeout = options.timeout ? parseInt(options.timeout, 10) : 0;
context.setDefaultTimeout(timeout);
context.setDefaultNavigationTimeout(timeout);
delete launchOptions.headless;
delete launchOptions.executablePath;
delete launchOptions.handleSIGINT;
delete contextOptions.deviceScaleFactor;
return { browser, browserName: browserType.name(), context, contextOptions, launchOptions, closeBrowser };
}
async function openPage(context, url) {
let page = context.pages()[0];
if (!page)
page = await context.newPage();
if (url) {
if (import_fs.default.existsSync(url))
url = "file://" + import_path.default.resolve(url);
else if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:") && !url.startsWith("data:"))
url = "http://" + url;
await page.goto(url);
}
return page;
}
async function open(options, url) {
const { context } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH });
await context._exposeConsoleApi();
await openPage(context, url);
}
async function codegen(options, url) {
const { target: language, output: outputFile, testIdAttribute: testIdAttributeName } = options;
const tracesDir = import_path.default.join(import_os.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
const { context, browser, launchOptions, contextOptions, closeBrowser } = await launchContext(options, {
headless: !!process.env.PWTEST_CLI_HEADLESS,
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH,
tracesDir
});
const donePromise = new import_utils.ManualPromise();
maybeSetupTestHooks(browser, closeBrowser, donePromise);
import_utilsBundle.dotenv.config({ path: "playwright.env" });
await context._enableRecorder({
language,
launchOptions,
contextOptions,
device: options.device,
saveStorage: options.saveStorage,
mode: "recording",
testIdAttributeName,
outputFile: outputFile ? import_path.default.resolve(outputFile) : void 0,
handleSIGINT: false
});
await openPage(context, url);
donePromise.resolve();
}
async function maybeSetupTestHooks(browser, closeBrowser, donePromise) {
if (!process.env.PWTEST_CLI_IS_UNDER_TEST)
return;
const logs = [];
require("playwright-core/lib/utilsBundle").debug.log = (...args) => {
const line = require("util").format(...args) + "\n";
logs.push(line);
process.stderr.write(line);
};
browser.on("disconnected", () => {
const hasCrashLine = logs.some((line) => line.includes("process did exit:") && !line.includes("process did exit: exitCode=0, signal=null"));
if (hasCrashLine) {
process.stderr.write("Detected browser crash.\n");
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
}
});
const close = async () => {
await donePromise;
await closeBrowser();
};
if (process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT) {
setTimeout(close, +process.env.PWTEST_CLI_EXIT_AFTER_TIMEOUT);
return;
}
let stdin = "";
process.stdin.on("data", (data) => {
stdin += data.toString();
if (stdin.startsWith("exit")) {
process.stdin.destroy();
close();
}
});
}
async function waitForPage(page, captureOptions) {
if (captureOptions.waitForSelector) {
console.log(`Waiting for selector ${captureOptions.waitForSelector}...`);
await page.waitForSelector(captureOptions.waitForSelector);
}
if (captureOptions.waitForTimeout) {
console.log(`Waiting for timeout ${captureOptions.waitForTimeout}...`);
await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10));
}
}
async function screenshot(options, captureOptions, url, path2) {
const { context } = await launchContext(options, { headless: true });
console.log("Navigating to " + url);
const page = await openPage(context, url);
await waitForPage(page, captureOptions);
console.log("Capturing screenshot into " + path2);
await page.screenshot({ path: path2, fullPage: !!captureOptions.fullPage });
await page.close();
}
async function pdf(options, captureOptions, url, path2) {
if (options.browser !== "chromium")
throw new Error("PDF creation is only working with Chromium");
const { context } = await launchContext({ ...options, browser: "chromium" }, { headless: true });
console.log("Navigating to " + url);
const page = await openPage(context, url);
await waitForPage(page, captureOptions);
console.log("Saving as pdf into " + path2);
await page.pdf({ path: path2, format: captureOptions.paperFormat });
await page.close();
}
function lookupBrowserType(options) {
let name = options.browser;
if (options.device) {
const device = playwright.devices[options.device];
name = device.defaultBrowserType;
}
let browserType;
switch (name) {
case "chromium":
browserType = playwright.chromium;
break;
case "webkit":
browserType = playwright.webkit;
break;
case "firefox":
browserType = playwright.firefox;
break;
case "cr":
browserType = playwright.chromium;
break;
case "wk":
browserType = playwright.webkit;
break;
case "ff":
browserType = playwright.firefox;
break;
}
if (browserType)
return browserType;
import_utilsBundle.program.help();
}
function validateOptions(options) {
if (options.device && !(options.device in playwright.devices)) {
const lines = [`Device descriptor not found: '${options.device}', available devices are:`];
for (const name in playwright.devices)
lines.push(` "${name}"`);
throw new Error(lines.join("\n"));
}
if (options.colorScheme && !["light", "dark"].includes(options.colorScheme))
throw new Error('Invalid color scheme, should be one of "light", "dark"');
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
codegen,
open,
pdf,
screenshot
});
+98
View File
@@ -0,0 +1,98 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var driver_exports = {};
__export(driver_exports, {
launchBrowserServer: () => launchBrowserServer,
printApiJson: () => printApiJson,
runDriver: () => runDriver,
runServer: () => runServer
});
module.exports = __toCommonJS(driver_exports);
var import_fs = __toESM(require("fs"));
var playwright = __toESM(require("../.."));
var import_pipeTransport = require("../server/utils/pipeTransport");
var import_playwrightServer = require("../remote/playwrightServer");
var import_server = require("../server");
var import_processLauncher = require("../server/utils/processLauncher");
function printApiJson() {
console.log(JSON.stringify(require("../../api.json")));
}
function runDriver() {
const dispatcherConnection = new import_server.DispatcherConnection();
new import_server.RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => {
const playwright2 = (0, import_server.createPlaywright)({ sdkLanguage });
return new import_server.PlaywrightDispatcher(rootScope, playwright2);
});
const transport = new import_pipeTransport.PipeTransport(process.stdout, process.stdin);
transport.onmessage = (message) => dispatcherConnection.dispatch(JSON.parse(message));
const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === "javascript";
const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value) => {
if (typeof value === "string")
return value.toWellFormed();
return value;
} : void 0;
dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer));
transport.onclose = () => {
dispatcherConnection.onmessage = () => {
};
(0, import_processLauncher.gracefullyProcessExitDoNotHang)(0);
};
process.on("SIGINT", () => {
});
}
async function runServer(options) {
const {
port,
host,
path = "/",
maxConnections = Infinity,
extension,
artifactsDir
} = options;
const server = new import_playwrightServer.PlaywrightServer({ mode: extension ? "extension" : "default", path, maxConnections, artifactsDir });
const wsEndpoint = await server.listen(port, host);
process.on("exit", () => server.close().catch(console.error));
console.log("Listening on " + wsEndpoint);
process.stdin.on("close", () => (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0));
}
async function launchBrowserServer(browserName, configFile) {
let options = {};
if (configFile)
options = JSON.parse(import_fs.default.readFileSync(configFile).toString());
const browserType = playwright[browserName];
const server = await browserType.launchServer(options);
console.log(server.wsEndpoint());
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
launchBrowserServer,
printApiJson,
runDriver,
runServer
});
+171
View File
@@ -0,0 +1,171 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var installActions_exports = {};
__export(installActions_exports, {
installBrowsers: () => installBrowsers,
installDeps: () => installDeps,
markDockerImage: () => markDockerImage,
registry: () => import_server.registry,
uninstallBrowsers: () => uninstallBrowsers
});
module.exports = __toCommonJS(installActions_exports);
var import_path = __toESM(require("path"));
var import_server = require("../server");
var import_utils = require("../utils");
var import_utils2 = require("../utils");
var import_ascii = require("../server/utils/ascii");
function printInstalledBrowsers(browsers) {
const browserPaths = /* @__PURE__ */ new Set();
for (const browser of browsers)
browserPaths.add(browser.browserPath);
console.log(` Browsers:`);
for (const browserPath of [...browserPaths].sort())
console.log(` ${browserPath}`);
console.log(` References:`);
const references = /* @__PURE__ */ new Set();
for (const browser of browsers)
references.add(browser.referenceDir);
for (const reference of [...references].sort())
console.log(` ${reference}`);
}
function printGroupedByPlaywrightVersion(browsers) {
const dirToVersion = /* @__PURE__ */ new Map();
for (const browser of browsers) {
if (dirToVersion.has(browser.referenceDir))
continue;
const packageJSON = require(import_path.default.join(browser.referenceDir, "package.json"));
const version = packageJSON.version;
dirToVersion.set(browser.referenceDir, version);
}
const groupedByPlaywrightMinorVersion = /* @__PURE__ */ new Map();
for (const browser of browsers) {
const version = dirToVersion.get(browser.referenceDir);
let entries = groupedByPlaywrightMinorVersion.get(version);
if (!entries) {
entries = [];
groupedByPlaywrightMinorVersion.set(version, entries);
}
entries.push(browser);
}
const sortedVersions = [...groupedByPlaywrightMinorVersion.keys()].sort((a, b) => {
const aComponents = a.split(".");
const bComponents = b.split(".");
const aMajor = parseInt(aComponents[0], 10);
const bMajor = parseInt(bComponents[0], 10);
if (aMajor !== bMajor)
return aMajor - bMajor;
const aMinor = parseInt(aComponents[1], 10);
const bMinor = parseInt(bComponents[1], 10);
if (aMinor !== bMinor)
return aMinor - bMinor;
return aComponents.slice(2).join(".").localeCompare(bComponents.slice(2).join("."));
});
for (const version of sortedVersions) {
console.log(`
Playwright version: ${version}`);
printInstalledBrowsers(groupedByPlaywrightMinorVersion.get(version));
}
}
async function markDockerImage(dockerImageNameTemplate) {
(0, import_utils2.assert)(dockerImageNameTemplate, "dockerImageNameTemplate is required");
await (0, import_server.writeDockerVersion)(dockerImageNameTemplate);
}
async function installBrowsers(args, options) {
if ((0, import_utils.isLikelyNpxGlobal)()) {
console.error((0, import_ascii.wrapInASCIIBox)([
`WARNING: It looks like you are running 'npx playwright install' without first`,
`installing your project's dependencies.`,
``,
`To avoid unexpected behavior, please install your dependencies first, and`,
`then run Playwright's install command:`,
``,
` npm install`,
` npx playwright install`,
``,
`If your project does not yet depend on Playwright, first install the`,
`applicable npm package (most commonly @playwright/test), and`,
`then run Playwright's install command to download the browsers:`,
``,
` npm install @playwright/test`,
` npx playwright install`,
``
].join("\n"), 1));
}
if (options.shell === false && options.onlyShell)
throw new Error(`Only one of --no-shell and --only-shell can be specified`);
const shell = options.shell === false ? "no" : options.onlyShell ? "only" : void 0;
const executables = import_server.registry.resolveBrowsers(args, { shell });
if (options.withDeps)
await import_server.registry.installDeps(executables, !!options.dryRun);
if (options.dryRun && options.list)
throw new Error(`Only one of --dry-run and --list can be specified`);
if (options.dryRun) {
for (const executable of executables) {
console.log(import_server.registry.calculateDownloadTitle(executable));
console.log(` Install location: ${executable.directory ?? "<system>"}`);
if (executable.downloadURLs?.length) {
const [url, ...fallbacks] = executable.downloadURLs;
console.log(` Download url: ${url}`);
for (let i = 0; i < fallbacks.length; ++i)
console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
}
console.log(``);
}
} else if (options.list) {
const browsers = await import_server.registry.listInstalledBrowsers();
printGroupedByPlaywrightVersion(browsers);
} else {
await import_server.registry.install(executables, { force: options.force });
await import_server.registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => {
e.name = "Playwright Host validation warning";
console.error(e);
});
}
}
async function uninstallBrowsers(options) {
delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC;
await import_server.registry.uninstall(!!options.all).then(({ numberOfBrowsersLeft }) => {
if (!options.all && numberOfBrowsersLeft > 0) {
console.log("Successfully uninstalled Playwright browsers for the current Playwright installation.");
console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations.
To uninstall Playwright browsers for all installations, re-run with --all flag.`);
}
});
}
async function installDeps(args, options) {
await import_server.registry.installDeps(import_server.registry.resolveBrowsers(args, {}), !!options.dryRun);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
installBrowsers,
installDeps,
markDockerImage,
registry,
uninstallBrowsers
});
+225
View File
@@ -0,0 +1,225 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var program_exports = {};
__export(program_exports, {
program: () => import_utilsBundle2.program
});
module.exports = __toCommonJS(program_exports);
var import_bootstrap = require("../bootstrap");
var import_utils = require("../utils");
var import_traceCli = require("../tools/trace/traceCli");
var import_utilsBundle = require("../utilsBundle");
var import_utilsBundle2 = require("../utilsBundle");
const packageJSON = require("../../package.json");
import_utilsBundle.program.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
import_utilsBundle.program.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(async function(dockerImageNameTemplate) {
const { markDockerImage } = require("./installActions");
markDockerImage(dockerImageNameTemplate).catch(logErrorAndExit);
});
commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(async function(url, options) {
const { open } = require("./browserActions");
open(options, url).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ open
$ open -b webkit https://example.com`);
commandWithOpenOptions(
"codegen [url]",
"open page and generate code for user actions",
[
["-o, --output <file name>", "saves the generated script to a file"],
["--target <language>", `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()],
["--test-id-attribute <attributeName>", "use the specified attribute to generate data test ID selectors"]
]
).action(async function(url, options) {
const { codegen } = require("./browserActions");
await codegen(options, url);
}).addHelpText("afterAll", `
Examples:
$ codegen
$ codegen --target=python
$ codegen -b webkit https://example.com`);
import_utilsBundle.program.command("install [browser...]").description("ensure browsers necessary for this version of Playwright are installed").option("--with-deps", "install system dependencies for browsers").option("--dry-run", "do not execute installation, only print information").option("--list", "prints list of browsers from all playwright installations").option("--force", "force reinstall of already installed browsers").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").action(async function(args, options) {
try {
const { installBrowsers } = require("./installActions");
await installBrowsers(args, options);
} catch (e) {
console.log(`Failed to install browsers
${e}`);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
}
}).addHelpText("afterAll", `
Examples:
- $ install
Install default browsers.
- $ install chrome firefox
Install custom browsers, supports chromium, firefox, webkit, chromium-headless-shell.`);
import_utilsBundle.program.command("uninstall").description("Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.").option("--all", "Removes all browsers used by any Playwright installation from the system.").action(async (options) => {
const { uninstallBrowsers } = require("./installActions");
uninstallBrowsers(options).catch(logErrorAndExit);
});
import_utilsBundle.program.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not execute installation commands, only print them").action(async function(args, options) {
try {
const { installDeps } = require("./installActions");
await installDeps(args, options);
} catch (e) {
console.log(`Failed to install browser dependencies
${e}`);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
}
}).addHelpText("afterAll", `
Examples:
- $ install-deps
Install dependencies for default browsers.
- $ install-deps chrome firefox
Install dependencies for specific browsers, supports chromium, firefox, webkit, chromium-headless-shell.`);
const browsers = [
{ alias: "cr", name: "Chromium", type: "chromium" },
{ alias: "ff", name: "Firefox", type: "firefox" },
{ alias: "wk", name: "WebKit", type: "webkit" }
];
for (const { alias, name, type } of browsers) {
commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(async function(url, options) {
const { open } = require("./browserActions");
open({ ...options, browser: type }, url).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ ${alias} https://example.com`);
}
commandWithOpenOptions(
"screenshot <url> <filename>",
"capture a page screenshot",
[
["--wait-for-selector <selector>", "wait for selector before taking a screenshot"],
["--wait-for-timeout <timeout>", "wait for timeout in milliseconds before taking a screenshot"],
["--full-page", "whether to take a full page screenshot (entire scrollable area)"]
]
).action(async function(url, filename, command) {
const { screenshot } = require("./browserActions");
screenshot(command, command, url, filename).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ screenshot -b webkit https://example.com example.png`);
commandWithOpenOptions(
"pdf <url> <filename>",
"save page as pdf",
[
["--paper-format <format>", "paper format: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6"],
["--wait-for-selector <selector>", "wait for given selector before saving as pdf"],
["--wait-for-timeout <timeout>", "wait for given timeout in milliseconds before saving as pdf"]
]
).action(async function(url, filename, options) {
const { pdf } = require("./browserActions");
pdf(options, options, url, filename).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ pdf https://example.com example.pdf`);
import_utilsBundle.program.command("run-driver", { hidden: true }).action(async function(options) {
const { runDriver } = require("./driver");
runDriver();
});
import_utilsBundle.program.command("run-server", { hidden: true }).option("--port <port>", "Server port").option("--host <host>", "Server host").option("--path <path>", "Endpoint Path", "/").option("--max-clients <maxClients>", "Maximum clients").option("--mode <mode>", 'Server mode, either "default" or "extension"').option("--artifacts-dir <artifactsDir>", "Artifacts directory").action(async function(options) {
const { runServer } = require("./driver");
runServer({
port: options.port ? +options.port : void 0,
host: options.host,
path: options.path,
maxConnections: options.maxClients ? +options.maxClients : Infinity,
extension: options.mode === "extension" || !!process.env.PW_EXTENSION_MODE,
artifactsDir: options.artifactsDir
}).catch(logErrorAndExit);
});
import_utilsBundle.program.command("print-api-json", { hidden: true }).action(async function(options) {
const { printApiJson } = require("./driver");
printApiJson();
});
import_utilsBundle.program.command("launch-server", { hidden: true }).requiredOption("--browser <browserName>", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config <path-to-config-file>", "JSON file with launchServer options").action(async function(options) {
const { launchBrowserServer } = require("./driver");
launchBrowserServer(options.browser, options.config);
});
import_utilsBundle.program.command("show-trace [trace]").option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("-h, --host <host>", "Host to serve trace on; specifying this option opens trace in a browser tab").option("-p, --port <port>", "Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab").option("--stdin", "Accept trace URLs over stdin to update the viewer").description("show trace viewer").action(async function(trace, options) {
if (options.browser === "cr")
options.browser = "chromium";
if (options.browser === "ff")
options.browser = "firefox";
if (options.browser === "wk")
options.browser = "webkit";
const openOptions = {
host: options.host,
port: +options.port,
isServer: !!options.stdin
};
const { runTraceInBrowser, runTraceViewerApp } = require("../server/trace/viewer/traceViewer");
if (options.port !== void 0 || options.host !== void 0)
runTraceInBrowser(trace, openOptions).catch(logErrorAndExit);
else
runTraceViewerApp(trace, options.browser, openOptions).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples:
$ show-trace
$ show-trace https://example.com/trace.zip`);
(0, import_traceCli.addTraceCommands)(import_utilsBundle.program, logErrorAndExit);
import_utilsBundle.program.command("cli", { hidden: true }).allowExcessArguments(true).allowUnknownOption(true).action(async (options) => {
const { program: cliProgram } = require("../tools/cli-client/program");
process.argv.splice(process.argv.indexOf("cli"), 1);
cliProgram().catch(logErrorAndExit);
});
function logErrorAndExit(e) {
if (process.env.PWDEBUGIMPL)
console.error(e);
else
console.error(e.name + ": " + e.message);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
}
function codegenId() {
return process.env.PW_LANG_NAME || "playwright-test";
}
function commandWithOpenOptions(command, description, options) {
let result = import_utilsBundle.program.command(command).description(description);
for (const option of options)
result = result.option(option[0], ...option.slice(1));
return result.option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("--block-service-workers", "block service workers").option("--channel <channel>", 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option("--color-scheme <scheme>", 'emulate preferred color scheme, "light" or "dark"').option("--device <deviceName>", 'emulate device, for example "iPhone 11"').option("--geolocation <coordinates>", 'specify geolocation coordinates, for example "37.819722,-122.478611"').option("--ignore-https-errors", "ignore https errors").option("--load-storage <filename>", "load context storage state from the file, previously saved with --save-storage").option("--lang <language>", 'specify language / locale, for example "en-GB"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--save-har <filename>", "save HAR file with all network activity at the end").option("--save-har-glob <glob pattern>", "filter entries in the HAR by matching url against this glob pattern").option("--save-storage <filename>", "save context storage state at the end, for later use with --load-storage").option("--timezone <time zone>", 'time zone to emulate, for example "Europe/Rome"').option("--timeout <timeout>", "timeout for Playwright actions in milliseconds, no timeout by default").option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <directory>", "use the specified user data directory instead of a new context").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280, 720"');
}
function buildBasePlaywrightCLICommand(cliTargetLang) {
switch (cliTargetLang) {
case "python":
return `playwright`;
case "java":
return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`;
case "csharp":
return `pwsh bin/Debug/netX/playwright.ps1`;
default: {
const packageManagerCommand = (0, import_utils.getPackageManagerExecCommand)();
return `${packageManagerCommand} playwright`;
}
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
program
});
+74
View File
@@ -0,0 +1,74 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var programWithTestStub_exports = {};
__export(programWithTestStub_exports, {
program: () => import_program2.program
});
module.exports = __toCommonJS(programWithTestStub_exports);
var import_processLauncher = require("../server/utils/processLauncher");
var import_utils = require("../utils");
var import_program = require("./program");
var import_program2 = require("./program");
function printPlaywrightTestError(command) {
const packages = [];
for (const pkg of ["playwright", "playwright-chromium", "playwright-firefox", "playwright-webkit"]) {
try {
require.resolve(pkg);
packages.push(pkg);
} catch (e) {
}
}
if (!packages.length)
packages.push("playwright");
const packageManager = (0, import_utils.getPackageManager)();
if (packageManager === "yarn") {
console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`);
console.error(` yarn remove ${packages.join(" ")}`);
console.error(" yarn add -D @playwright/test");
} else if (packageManager === "pnpm") {
console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`);
console.error(` pnpm remove ${packages.join(" ")}`);
console.error(" pnpm add -D @playwright/test");
} else {
console.error(`Please install @playwright/test package before running "npx playwright ${command}"`);
console.error(` npm uninstall ${packages.join(" ")}`);
console.error(" npm install -D @playwright/test");
}
}
const kExternalPlaywrightTestCommands = [
["test", "Run tests with Playwright Test."],
["show-report", "Show Playwright Test HTML report."],
["merge-reports", "Merge Playwright Test Blob reports"]
];
function addExternalPlaywrightTestCommands() {
for (const [command, description] of kExternalPlaywrightTestCommands) {
const playwrightTest = import_program.program.command(command).allowUnknownOption(true).allowExcessArguments(true);
playwrightTest.description(`${description} Available in @playwright/test package.`);
playwrightTest.action(async () => {
printPlaywrightTestError(command);
(0, import_processLauncher.gracefullyProcessExitDoNotHang)(1);
});
}
}
if (!process.env.PW_LANG_NAME)
addExternalPlaywrightTestCommands();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
program
});
+361
View File
@@ -0,0 +1,361 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var android_exports = {};
__export(android_exports, {
Android: () => Android,
AndroidDevice: () => AndroidDevice,
AndroidInput: () => AndroidInput,
AndroidSocket: () => AndroidSocket,
AndroidWebView: () => AndroidWebView
});
module.exports = __toCommonJS(android_exports);
var import_eventEmitter = require("./eventEmitter");
var import_browserContext = require("./browserContext");
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_events = require("./events");
var import_waiter = require("./waiter");
var import_timeoutSettings = require("./timeoutSettings");
var import_rtti = require("../utils/isomorphic/rtti");
var import_time = require("../utils/isomorphic/time");
var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
var import_connect = require("./connect");
class Android extends import_channelOwner.ChannelOwner {
static from(android) {
return android._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async devices(options = {}) {
const { devices } = await this._channel.devices(options);
return devices.map((d) => AndroidDevice.from(d));
}
async launchServer(options = {}) {
if (!this._serverLauncher)
throw new Error("Launching server is not supported");
return await this._serverLauncher.launchServer(options);
}
async connect(endpoint, options = {}) {
return await this._wrapApiCall(async () => {
const deadline = options.timeout ? (0, import_time.monotonicTime)() + options.timeout : 0;
const headers = { "x-playwright-browser": "android", ...options.headers };
const connectParams = { endpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 };
const connection = await (0, import_connect.connectToEndpoint)(this._connection, connectParams);
let device;
connection.on("close", () => {
device?._didClose();
});
const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
const playwright = await connection.initializePlaywright();
if (!playwright._initializer.preConnectedAndroidDevice) {
connection.close();
throw new Error("Malformed endpoint. Did you use Android.launchServer method?");
}
device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice);
device._shouldCloseConnectionOnClose = true;
device.on(import_events.Events.AndroidDevice.Close, () => connection.close());
return device;
}, deadline);
if (!result.timedOut) {
return result.result;
} else {
connection.close();
throw new Error(`Timeout ${options.timeout}ms exceeded`);
}
});
}
}
class AndroidDevice extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._webViews = /* @__PURE__ */ new Map();
this._shouldCloseConnectionOnClose = false;
this._android = parent;
this.input = new AndroidInput(this);
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform, parent._timeoutSettings);
this._channel.on("webViewAdded", ({ webView }) => this._onWebViewAdded(webView));
this._channel.on("webViewRemoved", ({ socketName }) => this._onWebViewRemoved(socketName));
this._channel.on("close", () => this._didClose());
}
static from(androidDevice) {
return androidDevice._object;
}
_onWebViewAdded(webView) {
const view = new AndroidWebView(this, webView);
this._webViews.set(webView.socketName, view);
this.emit(import_events.Events.AndroidDevice.WebView, view);
}
_onWebViewRemoved(socketName) {
const view = this._webViews.get(socketName);
this._webViews.delete(socketName);
if (view)
view.emit(import_events.Events.AndroidWebView.Close);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
serial() {
return this._initializer.serial;
}
model() {
return this._initializer.model;
}
webViews() {
return [...this._webViews.values()];
}
async webView(selector, options) {
const predicate = (v) => {
if (selector.pkg)
return v.pkg() === selector.pkg;
if (selector.socketName)
return v._socketName() === selector.socketName;
return false;
};
const webView = [...this._webViews.values()].find(predicate);
if (webView)
return webView;
return await this.waitForEvent("webview", { ...options, predicate });
}
async wait(selector, options = {}) {
await this._channel.wait({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) });
}
async fill(selector, text, options = {}) {
await this._channel.fill({ androidSelector: toSelectorChannel(selector), text, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async press(selector, key, options = {}) {
await this.tap(selector, options);
await this.input.press(key);
}
async tap(selector, options = {}) {
await this._channel.tap({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) });
}
async drag(selector, dest, options = {}) {
await this._channel.drag({ androidSelector: toSelectorChannel(selector), dest, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async fling(selector, direction, options = {}) {
await this._channel.fling({ androidSelector: toSelectorChannel(selector), direction, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async longTap(selector, options = {}) {
await this._channel.longTap({ androidSelector: toSelectorChannel(selector), ...options, timeout: this._timeoutSettings.timeout(options) });
}
async pinchClose(selector, percent, options = {}) {
await this._channel.pinchClose({ androidSelector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async pinchOpen(selector, percent, options = {}) {
await this._channel.pinchOpen({ androidSelector: toSelectorChannel(selector), percent, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async scroll(selector, direction, percent, options = {}) {
await this._channel.scroll({ androidSelector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async swipe(selector, direction, percent, options = {}) {
await this._channel.swipe({ androidSelector: toSelectorChannel(selector), direction, percent, ...options, timeout: this._timeoutSettings.timeout(options) });
}
async info(selector) {
return (await this._channel.info({ androidSelector: toSelectorChannel(selector) })).info;
}
async screenshot(options = {}) {
const { binary } = await this._channel.screenshot();
if (options.path)
await this._platform.fs().promises.writeFile(options.path, binary);
return binary;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close() {
try {
if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
_didClose() {
this.emit(import_events.Events.AndroidDevice.Close, this);
}
async shell(command) {
const { result } = await this._channel.shell({ command });
return result;
}
async open(command) {
return AndroidSocket.from((await this._channel.open({ command })).socket);
}
async installApk(file, options) {
await this._channel.installApk({ file: await loadFile(this._platform, file), args: options && options.args });
}
async push(file, path, options) {
await this._channel.push({ file: await loadFile(this._platform, file), path, mode: options ? options.mode : void 0 });
}
async launchBrowser(options = {}) {
const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
const result = await this._channel.launchBrowser(contextOptions);
const context = import_browserContext.BrowserContext.from(result.context);
const selectors = this._android._playwright.selectors;
selectors._contextsForSelectors.add(context);
context.once(import_events.Events.BrowserContext.Close, () => selectors._contextsForSelectors.delete(context));
await context._initializeHarFromOptions(options.recordHar);
return context;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== import_events.Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, import_events.Events.AndroidDevice.Close, () => new import_errors.TargetClosedError());
const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result;
});
}
}
class AndroidSocket extends import_channelOwner.ChannelOwner {
static from(androidDevice) {
return androidDevice._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._channel.on("data", ({ data }) => this.emit(import_events.Events.AndroidSocket.Data, data));
this._channel.on("close", () => this.emit(import_events.Events.AndroidSocket.Close));
}
async write(data) {
await this._channel.write({ data });
}
async close() {
await this._channel.close();
}
async [Symbol.asyncDispose]() {
await this.close();
}
}
async function loadFile(platform, file) {
if ((0, import_rtti.isString)(file))
return await platform.fs().promises.readFile(file);
return file;
}
class AndroidInput {
constructor(device) {
this._device = device;
}
async type(text) {
await this._device._channel.inputType({ text });
}
async press(key) {
await this._device._channel.inputPress({ key });
}
async tap(point) {
await this._device._channel.inputTap({ point });
}
async swipe(from, segments, steps) {
await this._device._channel.inputSwipe({ segments, steps });
}
async drag(from, to, steps) {
await this._device._channel.inputDrag({ from, to, steps });
}
}
function toSelectorChannel(selector) {
const {
checkable,
checked,
clazz,
clickable,
depth,
desc,
enabled,
focusable,
focused,
hasChild,
hasDescendant,
longClickable,
pkg,
res,
scrollable,
selected,
text
} = selector;
const toRegex = (value) => {
if (value === void 0)
return void 0;
if ((0, import_rtti.isRegExp)(value))
return value.source;
return "^" + value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d") + "$";
};
return {
checkable,
checked,
clazz: toRegex(clazz),
pkg: toRegex(pkg),
desc: toRegex(desc),
res: toRegex(res),
text: toRegex(text),
clickable,
depth,
enabled,
focusable,
focused,
hasChild: hasChild ? { androidSelector: toSelectorChannel(hasChild.selector) } : void 0,
hasDescendant: hasDescendant ? { androidSelector: toSelectorChannel(hasDescendant.selector), maxDepth: hasDescendant.maxDepth } : void 0,
longClickable,
scrollable,
selected
};
}
class AndroidWebView extends import_eventEmitter.EventEmitter {
constructor(device, data) {
super(device._platform);
this._device = device;
this._data = data;
}
pid() {
return this._data.pid;
}
pkg() {
return this._data.pkg;
}
_socketName() {
return this._data.socketName;
}
async page() {
if (!this._pagePromise)
this._pagePromise = this._fetchPage();
return await this._pagePromise;
}
async _fetchPage() {
const { context } = await this._device._channel.connectToWebView({ socketName: this._data.socketName });
return import_browserContext.BrowserContext.from(context).pages()[0];
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Android,
AndroidDevice,
AndroidInput,
AndroidSocket,
AndroidWebView
});
+137
View File
@@ -0,0 +1,137 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var api_exports = {};
__export(api_exports, {
APIRequest: () => import_fetch.APIRequest,
APIRequestContext: () => import_fetch.APIRequestContext,
APIResponse: () => import_fetch.APIResponse,
Android: () => import_android.Android,
AndroidDevice: () => import_android.AndroidDevice,
AndroidInput: () => import_android.AndroidInput,
AndroidSocket: () => import_android.AndroidSocket,
AndroidWebView: () => import_android.AndroidWebView,
Browser: () => import_browser.Browser,
BrowserContext: () => import_browserContext.BrowserContext,
BrowserType: () => import_browserType.BrowserType,
CDPSession: () => import_cdpSession.CDPSession,
Clock: () => import_clock.Clock,
ConsoleMessage: () => import_consoleMessage.ConsoleMessage,
Coverage: () => import_coverage.Coverage,
Debugger: () => import_debugger.Debugger,
Dialog: () => import_dialog.Dialog,
Download: () => import_download.Download,
Electron: () => import_electron.Electron,
ElectronApplication: () => import_electron.ElectronApplication,
ElementHandle: () => import_elementHandle.ElementHandle,
FileChooser: () => import_fileChooser.FileChooser,
Frame: () => import_frame.Frame,
FrameLocator: () => import_locator.FrameLocator,
JSHandle: () => import_jsHandle.JSHandle,
Keyboard: () => import_input.Keyboard,
Locator: () => import_locator.Locator,
Mouse: () => import_input.Mouse,
Page: () => import_page.Page,
Playwright: () => import_playwright.Playwright,
Request: () => import_network.Request,
Response: () => import_network.Response,
Route: () => import_network.Route,
Selectors: () => import_selectors.Selectors,
TimeoutError: () => import_errors.TimeoutError,
Touchscreen: () => import_input.Touchscreen,
Tracing: () => import_tracing.Tracing,
Video: () => import_video.Video,
WebError: () => import_webError.WebError,
WebSocket: () => import_network.WebSocket,
WebSocketRoute: () => import_network.WebSocketRoute,
Worker: () => import_worker.Worker
});
module.exports = __toCommonJS(api_exports);
var import_android = require("./android");
var import_browser = require("./browser");
var import_browserContext = require("./browserContext");
var import_browserType = require("./browserType");
var import_clock = require("./clock");
var import_consoleMessage = require("./consoleMessage");
var import_coverage = require("./coverage");
var import_debugger = require("./debugger");
var import_dialog = require("./dialog");
var import_download = require("./download");
var import_electron = require("./electron");
var import_locator = require("./locator");
var import_elementHandle = require("./elementHandle");
var import_fileChooser = require("./fileChooser");
var import_errors = require("./errors");
var import_frame = require("./frame");
var import_input = require("./input");
var import_jsHandle = require("./jsHandle");
var import_network = require("./network");
var import_fetch = require("./fetch");
var import_page = require("./page");
var import_selectors = require("./selectors");
var import_tracing = require("./tracing");
var import_video = require("./video");
var import_worker = require("./worker");
var import_cdpSession = require("./cdpSession");
var import_playwright = require("./playwright");
var import_webError = require("./webError");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
APIRequest,
APIRequestContext,
APIResponse,
Android,
AndroidDevice,
AndroidInput,
AndroidSocket,
AndroidWebView,
Browser,
BrowserContext,
BrowserType,
CDPSession,
Clock,
ConsoleMessage,
Coverage,
Debugger,
Dialog,
Download,
Electron,
ElectronApplication,
ElementHandle,
FileChooser,
Frame,
FrameLocator,
JSHandle,
Keyboard,
Locator,
Mouse,
Page,
Playwright,
Request,
Response,
Route,
Selectors,
TimeoutError,
Touchscreen,
Tracing,
Video,
WebError,
WebSocket,
WebSocketRoute,
Worker
});
+79
View File
@@ -0,0 +1,79 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var artifact_exports = {};
__export(artifact_exports, {
Artifact: () => Artifact
});
module.exports = __toCommonJS(artifact_exports);
var import_channelOwner = require("./channelOwner");
var import_stream = require("./stream");
var import_fileUtils = require("./fileUtils");
class Artifact extends import_channelOwner.ChannelOwner {
static from(channel) {
return channel._object;
}
async pathAfterFinished() {
if (this._connection.isRemote())
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
return (await this._channel.pathAfterFinished()).value;
}
async saveAs(path) {
if (!this._connection.isRemote()) {
await this._channel.saveAs({ path });
return;
}
const result = await this._channel.saveAsStream();
const stream = import_stream.Stream.from(result.stream);
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, path);
await new Promise((resolve, reject) => {
stream.stream().pipe(this._platform.fs().createWriteStream(path)).on("finish", resolve).on("error", reject);
});
}
async failure() {
return (await this._channel.failure()).error || null;
}
async createReadStream() {
const result = await this._channel.stream();
const stream = import_stream.Stream.from(result.stream);
return stream.stream();
}
async readIntoBuffer() {
const stream = await this.createReadStream();
return await new Promise((resolve, reject) => {
const chunks = [];
stream.on("data", (chunk) => {
chunks.push(chunk);
});
stream.on("end", () => {
resolve(Buffer.concat(chunks));
});
stream.on("error", reject);
});
}
async cancel() {
return await this._channel.cancel();
}
async delete() {
return await this._channel.delete();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Artifact
});
+169
View File
@@ -0,0 +1,169 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browser_exports = {};
__export(browser_exports, {
Browser: () => Browser
});
module.exports = __toCommonJS(browser_exports);
var import_artifact = require("./artifact");
var import_browserContext = require("./browserContext");
var import_cdpSession = require("./cdpSession");
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_events = require("./events");
var import_fileUtils = require("./fileUtils");
class Browser extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._contexts = /* @__PURE__ */ new Set();
this._isConnected = true;
this._shouldCloseConnectionOnClose = false;
this._options = {};
this._name = initializer.name;
this._browserName = initializer.browserName;
this._channel.on("context", ({ context }) => this._didCreateContext(import_browserContext.BrowserContext.from(context)));
this._channel.on("close", () => this._didClose());
this._closedPromise = new Promise((f) => this.once(import_events.Events.Browser.Disconnected, f));
}
static from(browser) {
return browser._object;
}
browserType() {
return this._browserType;
}
async newContext(options = {}) {
return await this._innerNewContext(options, false);
}
async _newContextForReuse(options = {}) {
return await this._innerNewContext(options, true);
}
async _disconnectFromReusedContext(reason) {
const context = [...this._contexts].find((context2) => context2._forReuse);
if (!context)
return;
await this._instrumentation.runBeforeCloseBrowserContext(context);
for (const page of context.pages())
page._onClose();
context._onClose();
await this._channel.disconnectFromReusedContext({ reason });
}
async _innerNewContext(userOptions = {}, forReuse) {
const options = this._browserType._playwright.selectors._withSelectorOptions(userOptions);
await this._instrumentation.runBeforeCreateBrowserContext(options);
const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
const context = import_browserContext.BrowserContext.from(response.context);
if (forReuse)
context._forReuse = true;
if (options.logger)
context._logger = options.logger;
await context._initializeHarFromOptions(options.recordHar);
await this._instrumentation.runAfterCreateBrowserContext(context);
return context;
}
_connectToBrowserType(browserType, browserOptions, logger) {
this._browserType = browserType;
this._options = browserOptions;
this._logger = logger;
for (const context of this._contexts)
this._setupBrowserContext(context);
}
_didCreateContext(context) {
context._browser = this;
this._contexts.add(context);
if (this._browserType)
this._setupBrowserContext(context);
}
_setupBrowserContext(context) {
context._logger = this._logger;
context.tracing._tracesDir = this._options.tracesDir;
this._browserType._contexts.add(context);
this._browserType._playwright.selectors._contextsForSelectors.add(context);
context.setDefaultTimeout(this._browserType._playwright._defaultContextTimeout);
context.setDefaultNavigationTimeout(this._browserType._playwright._defaultContextNavigationTimeout);
}
contexts() {
return [...this._contexts];
}
version() {
return this._initializer.version;
}
async bind(title, options = {}) {
const { endpoint } = await this._channel.startServer({ title, ...options });
return { endpoint };
}
async unbind() {
await this._channel.stopServer();
}
async newPage(options = {}) {
return await this._wrapApiCall(async () => {
const context = await this.newContext(options);
const page = await context.newPage();
page._ownedContext = context;
context._ownerPage = page;
return page;
}, { title: "Create page" });
}
isConnected() {
return this._isConnected;
}
async newBrowserCDPSession() {
return import_cdpSession.CDPSession.from((await this._channel.newBrowserCDPSession()).session);
}
async startTracing(page, options = {}) {
this._path = options.path;
await this._channel.startTracing({ ...options, page: page ? page._channel : void 0 });
}
async stopTracing() {
const artifact = import_artifact.Artifact.from((await this._channel.stopTracing()).artifact);
const buffer = await artifact.readIntoBuffer();
await artifact.delete();
if (this._path) {
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, this._path);
await this._platform.fs().promises.writeFile(this._path, buffer);
this._path = void 0;
}
return buffer;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options = {}) {
this._closeReason = options.reason;
try {
if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close(options);
await this._closedPromise;
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
_didClose() {
this._isConnected = false;
this.emit(import_events.Events.Browser.Disconnected, this);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Browser
});
+563
View File
@@ -0,0 +1,563 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserContext_exports = {};
__export(browserContext_exports, {
BrowserContext: () => BrowserContext,
prepareBrowserContextParams: () => prepareBrowserContextParams,
toClientCertificatesProtocol: () => toClientCertificatesProtocol
});
module.exports = __toCommonJS(browserContext_exports);
var import_artifact = require("./artifact");
var import_cdpSession = require("./cdpSession");
var import_channelOwner = require("./channelOwner");
var import_clientHelper = require("./clientHelper");
var import_clock = require("./clock");
var import_consoleMessage = require("./consoleMessage");
var import_debugger = require("./debugger");
var import_dialog = require("./dialog");
var import_disposable = require("./disposable");
var import_errors = require("./errors");
var import_events = require("./events");
var import_fetch = require("./fetch");
var import_frame = require("./frame");
var import_harRouter = require("./harRouter");
var network = __toESM(require("./network"));
var import_page = require("./page");
var import_tracing = require("./tracing");
var import_waiter = require("./waiter");
var import_webError = require("./webError");
var import_worker = require("./worker");
var import_timeoutSettings = require("./timeoutSettings");
var import_fileUtils = require("./fileUtils");
var import_headers = require("../utils/isomorphic/headers");
var import_urlMatch = require("../utils/isomorphic/urlMatch");
var import_rtti = require("../utils/isomorphic/rtti");
var import_stackTrace = require("../utils/isomorphic/stackTrace");
class BrowserContext extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._pages = /* @__PURE__ */ new Set();
this._routes = [];
this._webSocketRoutes = [];
// Browser is null for browser contexts created outside of normal browser, e.g. android or electron.
this._browser = null;
this._bindings = /* @__PURE__ */ new Map();
this._forReuse = false;
this._serviceWorkers = /* @__PURE__ */ new Set();
this._harRecorders = /* @__PURE__ */ new Map();
this._closingStatus = "none";
this._harRouters = [];
this._options = initializer.options;
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
this.debugger = import_debugger.Debugger.from(initializer.debugger);
this.tracing = import_tracing.Tracing.from(initializer.tracing);
this.request = import_fetch.APIRequestContext.from(initializer.requestContext);
this.request._timeoutSettings = this._timeoutSettings;
this.clock = new import_clock.Clock(this);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(import_page.BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("page", ({ page }) => this._onPage(import_page.Page.from(page)));
this._channel.on("route", ({ route }) => this._onRoute(network.Route.from(route)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
this._channel.on("serviceWorker", ({ worker }) => {
const serviceWorker = import_worker.Worker.from(worker);
serviceWorker._context = this;
this._serviceWorkers.add(serviceWorker);
this.emit(import_events.Events.BrowserContext.ServiceWorker, serviceWorker);
});
this._channel.on("console", (event) => {
const worker = import_worker.Worker.fromNullable(event.worker);
const page = import_page.Page.fromNullable(event.page);
const consoleMessage = new import_consoleMessage.ConsoleMessage(this._platform, event, page, worker);
worker?.emit(import_events.Events.Worker.Console, consoleMessage);
page?.emit(import_events.Events.Page.Console, consoleMessage);
if (worker && this._serviceWorkers.has(worker)) {
const scope = this._serviceWorkerScope(worker);
for (const page2 of this._pages) {
if (scope && page2.url().startsWith(scope))
page2.emit(import_events.Events.Page.Console, consoleMessage);
}
}
this.emit(import_events.Events.BrowserContext.Console, consoleMessage);
});
this._channel.on("pageError", ({ error, page }) => {
const pageObject = import_page.Page.from(page);
const parsedError = (0, import_errors.parseError)(error);
this.emit(import_events.Events.BrowserContext.WebError, new import_webError.WebError(pageObject, parsedError));
if (pageObject)
pageObject.emit(import_events.Events.Page.PageError, parsedError);
});
this._channel.on("dialog", ({ dialog }) => {
const dialogObject = import_dialog.Dialog.from(dialog);
let hasListeners = this.emit(import_events.Events.BrowserContext.Dialog, dialogObject);
const page = dialogObject.page();
if (page)
hasListeners = page.emit(import_events.Events.Page.Dialog, dialogObject) || hasListeners;
if (!hasListeners) {
if (dialogObject.type() === "beforeunload")
dialog.accept({}).catch(() => {
});
else
dialog.dismiss().catch(() => {
});
}
});
this._channel.on("request", ({ request, page }) => this._onRequest(network.Request.from(request), import_page.Page.fromNullable(page)));
this._channel.on("requestFailed", ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, import_page.Page.fromNullable(page)));
this._channel.on("requestFinished", (params) => this._onRequestFinished(params));
this._channel.on("response", ({ response, page }) => this._onResponse(network.Response.from(response), import_page.Page.fromNullable(page)));
this._channel.on("recorderEvent", ({ event, data, page, code }) => {
if (event === "actionAdded")
this._onRecorderEventSink?.actionAdded?.(import_page.Page.from(page), data, code);
else if (event === "actionUpdated")
this._onRecorderEventSink?.actionUpdated?.(import_page.Page.from(page), data, code);
else if (event === "signalAdded")
this._onRecorderEventSink?.signalAdded?.(import_page.Page.from(page), data);
});
this._closedPromise = new Promise((f) => this.once(import_events.Events.BrowserContext.Close, f));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.BrowserContext.Console, "console"],
[import_events.Events.BrowserContext.Dialog, "dialog"],
[import_events.Events.BrowserContext.Request, "request"],
[import_events.Events.BrowserContext.Response, "response"],
[import_events.Events.BrowserContext.RequestFinished, "requestFinished"],
[import_events.Events.BrowserContext.RequestFailed, "requestFailed"]
]));
}
static from(context) {
return context._object;
}
static fromNullable(context) {
return context ? BrowserContext.from(context) : null;
}
async _initializeHarFromOptions(recordHar) {
if (!recordHar)
return;
const defaultContent = recordHar.path.endsWith(".zip") ? "attach" : "embed";
await this._recordIntoHAR(recordHar.path, null, {
url: recordHar.urlFilter,
updateContent: recordHar.content ?? (recordHar.omitContent ? "omit" : defaultContent),
updateMode: recordHar.mode ?? "full"
});
}
_onPage(page) {
this._pages.add(page);
this.emit(import_events.Events.BrowserContext.Page, page);
if (page._opener && !page._opener.isClosed())
page._opener.emit(import_events.Events.Page.Popup, page);
}
_onRequest(request, page) {
this.emit(import_events.Events.BrowserContext.Request, request);
if (page)
page.emit(import_events.Events.Page.Request, request);
}
_onResponse(response, page) {
this.emit(import_events.Events.BrowserContext.Response, response);
if (page)
page.emit(import_events.Events.Page.Response, response);
}
_onRequestFailed(request, responseEndTiming, failureText, page) {
request._failureText = failureText || null;
request._setResponseEndTiming(responseEndTiming);
this.emit(import_events.Events.BrowserContext.RequestFailed, request);
if (page)
page.emit(import_events.Events.Page.RequestFailed, request);
}
_onRequestFinished(params) {
const { responseEndTiming } = params;
const request = network.Request.from(params.request);
const response = network.Response.fromNullable(params.response);
const page = import_page.Page.fromNullable(params.page);
request._setResponseEndTiming(responseEndTiming);
this.emit(import_events.Events.BrowserContext.RequestFinished, request);
if (page)
page.emit(import_events.Events.Page.RequestFinished, request);
if (response)
response._finishedPromise.resolve(null);
}
async _onRoute(route) {
route._context = this;
const page = route.request()._safePage();
const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) {
if (page?._closeWasCalled || this.isClosed())
return;
if (!routeHandler.matches(route.request().url()))
continue;
const index = this._routes.indexOf(routeHandler);
if (index === -1)
continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route);
if (!this._routes.length)
this._updateInterceptionPatterns({ internal: true }).catch(() => {
});
if (handled)
return;
}
await route._innerContinue(
true
/* isFallback */
).catch(() => {
});
}
async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find((route) => route.matches(webSocketRoute.url()));
if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
webSocketRoute.connectToServer();
}
async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name);
if (!func)
return;
await bindingCall.call(func);
}
_serviceWorkerScope(serviceWorker) {
try {
let url = new URL(".", serviceWorker.url()).href;
if (!url.endsWith("/"))
url += "/";
return url;
} catch {
return null;
}
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
browser() {
return this._browser;
}
pages() {
return [...this._pages];
}
isClosed() {
return this._closingStatus !== "none";
}
async newPage() {
if (this._ownerPage)
throw new Error("Please use browser.newContext()");
return import_page.Page.from((await this._channel.newPage()).page);
}
async cookies(urls) {
if (!urls)
urls = [];
if (urls && typeof urls === "string")
urls = [urls];
return (await this._channel.cookies({ urls })).cookies;
}
async addCookies(cookies) {
await this._channel.addCookies({ cookies });
}
async clearCookies(options = {}) {
await this._channel.clearCookies({
name: (0, import_rtti.isString)(options.name) ? options.name : void 0,
nameRegexSource: (0, import_rtti.isRegExp)(options.name) ? options.name.source : void 0,
nameRegexFlags: (0, import_rtti.isRegExp)(options.name) ? options.name.flags : void 0,
domain: (0, import_rtti.isString)(options.domain) ? options.domain : void 0,
domainRegexSource: (0, import_rtti.isRegExp)(options.domain) ? options.domain.source : void 0,
domainRegexFlags: (0, import_rtti.isRegExp)(options.domain) ? options.domain.flags : void 0,
path: (0, import_rtti.isString)(options.path) ? options.path : void 0,
pathRegexSource: (0, import_rtti.isRegExp)(options.path) ? options.path.source : void 0,
pathRegexFlags: (0, import_rtti.isRegExp)(options.path) ? options.path.flags : void 0
});
}
async grantPermissions(permissions, options) {
await this._channel.grantPermissions({ permissions, ...options });
}
async clearPermissions() {
await this._channel.clearPermissions();
}
async setGeolocation(geolocation) {
await this._channel.setGeolocation({ geolocation: geolocation || void 0 });
}
async setExtraHTTPHeaders(headers) {
network.validateHeaders(headers);
await this._channel.setExtraHTTPHeaders({ headers: (0, import_headers.headersObjectToArray)(headers) });
}
async setOffline(offline) {
await this._channel.setOffline({ offline });
}
async setHTTPCredentials(httpCredentials) {
await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || void 0 });
}
async addInitScript(script, arg) {
const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
return import_disposable.DisposableObject.from((await this._channel.addInitScript({ source })).disposable);
}
async exposeBinding(name, callback, options = {}) {
const result = await this._channel.exposeBinding({ name, needsHandle: options.handle });
this._bindings.set(name, callback);
return import_disposable.DisposableObject.from(result.disposable);
}
async exposeFunction(name, callback) {
const result = await this._channel.exposeBinding({ name });
const binding = (source, ...args) => callback(...args);
this._bindings.set(name, binding);
return import_disposable.DisposableObject.from(result.disposable);
}
async route(url, handler, options = {}) {
this._routes.unshift(new network.RouteHandler(this._platform, this._options.baseURL, url, handler, options.times));
await this._updateInterceptionPatterns({ title: "Route requests" });
return new import_disposable.DisposableStub(() => this.unroute(url, handler));
}
async routeWebSocket(url, handler) {
this._webSocketRoutes.unshift(new network.WebSocketRouteHandler(this._options.baseURL, url, handler));
await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
}
async _recordIntoHAR(har, page, options = {}) {
const { harId } = await this._channel.harStart({
page: page?._channel,
options: {
zip: har.endsWith(".zip"),
content: options.updateContent ?? "attach",
urlGlob: (0, import_rtti.isString)(options.url) ? options.url : void 0,
urlRegexSource: (0, import_rtti.isRegExp)(options.url) ? options.url.source : void 0,
urlRegexFlags: (0, import_rtti.isRegExp)(options.url) ? options.url.flags : void 0,
mode: options.updateMode ?? "minimal"
}
});
this._harRecorders.set(harId, { path: har, content: options.updateContent ?? "attach" });
}
async routeFromHAR(har, options = {}) {
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options.update) {
await this._recordIntoHAR(har, null, options);
return;
}
const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
this._harRouters.push(harRouter);
await harRouter.addContextRoute(this);
}
_disposeHarRouters() {
this._harRouters.forEach((router) => router.dispose());
this._harRouters = [];
}
async unrouteAll(options) {
await this._unrouteInternal(this._routes, [], options?.behavior);
this._disposeHarRouters();
}
async unroute(url, handler) {
const removed = [];
const remaining = [];
for (const route of this._routes) {
if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
removed.push(route);
else
remaining.push(route);
}
await this._unrouteInternal(removed, remaining, "default");
}
async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining;
if (behavior && behavior !== "default") {
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises);
}
await this._updateInterceptionPatterns({ title: "Unroute requests" });
}
async _updateInterceptionPatterns(options) {
const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes);
await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options);
}
async _updateWebSocketInterceptionPatterns(options) {
const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options);
}
_effectiveCloseReason() {
return this._closeReason || this._browser?._closeReason;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== import_events.Events.BrowserContext.Close)
waiter.rejectOnEvent(this, import_events.Events.BrowserContext.Close, () => new import_errors.TargetClosedError(this._effectiveCloseReason()));
const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result;
});
}
async storageState(options = {}) {
const state = await this._channel.storageState({ indexedDB: options.indexedDB });
if (options.path) {
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
}
return state;
}
async setStorageState(storageState) {
const state = await prepareStorageState(this._platform, storageState);
await this._channel.setStorageState({ storageState: state });
}
backgroundPages() {
return [];
}
serviceWorkers() {
return [...this._serviceWorkers];
}
async newCDPSession(page) {
if (!(page instanceof import_page.Page) && !(page instanceof import_frame.Frame))
throw new Error("page: expected Page or Frame");
const result = await this._channel.newCDPSession(page instanceof import_page.Page ? { page: page._channel } : { frame: page._channel });
return import_cdpSession.CDPSession.from(result.session);
}
_onClose() {
this._closingStatus = "closed";
this._browser?._contexts.delete(this);
this._browser?._browserType._contexts.delete(this);
this._browser?._browserType._playwright.selectors._contextsForSelectors.delete(this);
this._disposeHarRouters();
this.tracing._resetStackCounter();
this.emit(import_events.Events.BrowserContext.Close, this);
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options = {}) {
if (this.isClosed())
return;
this._closeReason = options.reason;
this._closingStatus = "closing";
await this.request.dispose(options);
await this._instrumentation.runBeforeCloseBrowserContext(this);
await this._wrapApiCall(async () => {
for (const [harId, harParams] of this._harRecorders) {
const har = await this._channel.harExport({ harId });
const artifact = import_artifact.Artifact.from(har.artifact);
const isCompressed = harParams.content === "attach" || harParams.path.endsWith(".zip");
const needCompressed = harParams.path.endsWith(".zip");
if (isCompressed && !needCompressed) {
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Uncompressed har is not supported in thin clients");
await artifact.saveAs(harParams.path + ".tmp");
await localUtils.harUnzip({ zipFile: harParams.path + ".tmp", harFile: harParams.path });
} else {
await artifact.saveAs(harParams.path);
}
await artifact.delete();
}
}, { internal: true });
await this._channel.close(options);
await this._closedPromise;
}
async _enableRecorder(params, eventSink) {
if (eventSink)
this._onRecorderEventSink = eventSink;
await this._channel.enableRecorder(params);
}
async _disableRecorder() {
this._onRecorderEventSink = void 0;
await this._channel.disableRecorder();
}
async _exposeConsoleApi() {
await this._channel.exposeConsoleApi();
}
}
async function prepareStorageState(platform, storageState) {
if (typeof storageState !== "string")
return storageState;
try {
return JSON.parse(await platform.fs().promises.readFile(storageState, "utf8"));
} catch (e) {
(0, import_stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${storageState}:
` + e.message);
throw e;
}
}
async function prepareBrowserContextParams(platform, options) {
if (options.videoSize && !options.videosPath)
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
if (options.extraHTTPHeaders)
network.validateHeaders(options.extraHTTPHeaders);
const contextParams = {
...options,
viewport: options.viewport === null ? void 0 : options.viewport,
noDefaultViewport: options.viewport === null,
extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
storageState: options.storageState ? await prepareStorageState(platform, options.storageState) : void 0,
serviceWorkers: options.serviceWorkers,
colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme,
reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion,
forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors,
contrast: options.contrast === null ? "no-override" : options.contrast,
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates)
};
if (!contextParams.recordVideo && options.videosPath) {
contextParams.recordVideo = {
dir: options.videosPath,
size: options.videoSize
};
}
if (contextParams.recordVideo && contextParams.recordVideo.dir)
contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
return contextParams;
}
function toAcceptDownloadsProtocol(acceptDownloads) {
if (acceptDownloads === void 0)
return void 0;
if (acceptDownloads)
return "accept";
return "deny";
}
async function toClientCertificatesProtocol(platform, certs) {
if (!certs)
return void 0;
const bufferizeContent = async (value, path) => {
if (value)
return value;
if (path)
return await platform.fs().promises.readFile(path);
};
return await Promise.all(certs.map(async (cert) => ({
origin: cert.origin,
cert: await bufferizeContent(cert.cert, cert.certPath),
key: await bufferizeContent(cert.key, cert.keyPath),
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
passphrase: cert.passphrase
})));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserContext,
prepareBrowserContextParams,
toClientCertificatesProtocol
});
+153
View File
@@ -0,0 +1,153 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserType_exports = {};
__export(browserType_exports, {
BrowserType: () => BrowserType
});
module.exports = __toCommonJS(browserType_exports);
var import_browser = require("./browser");
var import_browserContext = require("./browserContext");
var import_channelOwner = require("./channelOwner");
var import_clientHelper = require("./clientHelper");
var import_assert = require("../utils/isomorphic/assert");
var import_headers = require("../utils/isomorphic/headers");
var import_connect = require("./connect");
var import_timeoutSettings = require("./timeoutSettings");
class BrowserType extends import_channelOwner.ChannelOwner {
constructor() {
super(...arguments);
this._contexts = /* @__PURE__ */ new Set();
}
static from(browserType) {
return browserType._object;
}
executablePath() {
if (!this._initializer.executablePath)
throw new Error("Browser is not supported on current platform");
return this._initializer.executablePath;
}
name() {
return this._initializer.name;
}
async launch(options = {}) {
(0, import_assert.assert)(!options.userDataDir, "userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead");
(0, import_assert.assert)(!options.port, "Cannot specify a port without launching as a server.");
const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
options = { ...this._playwright._defaultLaunchOptions, ...options };
const launchOptions = {
...options,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0,
timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
};
return await this._wrapApiCall(async () => {
const browser = import_browser.Browser.from((await this._channel.launch(launchOptions)).browser);
browser._connectToBrowserType(this, options, logger);
return browser;
});
}
async launchServer(options = {}) {
if (!this._serverLauncher)
throw new Error("Launching server is not supported");
options = { ...this._playwright._defaultLaunchOptions, ...options };
return await this._serverLauncher.launchServer(options);
}
async launchPersistentContext(userDataDir, options = {}) {
(0, import_assert.assert)(!options.port, "Cannot specify a port without launching as a server.");
options = this._playwright.selectors._withSelectorOptions({
...this._playwright._defaultLaunchOptions,
...options
});
await this._instrumentation.runBeforeCreateBrowserContext(options);
const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
const contextParams = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
const persistentParams = {
...contextParams,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0,
channel: options.channel,
userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir),
timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
};
const context = await this._wrapApiCall(async () => {
const result = await this._channel.launchPersistentContext(persistentParams);
const browser = import_browser.Browser.from(result.browser);
browser._connectToBrowserType(this, options, logger);
const context2 = import_browserContext.BrowserContext.from(result.context);
await context2._initializeHarFromOptions(options.recordHar);
return context2;
});
await this._instrumentation.runAfterCreateBrowserContext(context);
return context;
}
async connect(optionsOrEndpoint, options) {
if (typeof optionsOrEndpoint === "string")
return await this._connect({ ...options, endpoint: optionsOrEndpoint });
(0, import_assert.assert)(optionsOrEndpoint.wsEndpoint, "options.wsEndpoint is required");
return await this._connect({ ...options, endpoint: optionsOrEndpoint.wsEndpoint });
}
async _connect(params) {
const logger = params.logger;
return await this._wrapApiCall(async () => {
const browser = await (0, import_connect.connectToBrowser)(this._playwright, { browserName: this.name(), ...params });
browser._connectToBrowserType(this, {}, logger);
return browser;
});
}
async connectOverCDP(endpointURLOrOptions, options) {
if (typeof endpointURLOrOptions === "string")
return await this._connectOverCDP(endpointURLOrOptions, options);
const endpointURL = "endpointURL" in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint;
(0, import_assert.assert)(endpointURL, "Cannot connect over CDP without wsEndpoint.");
return await this.connectOverCDP(endpointURL, endpointURLOrOptions);
}
async _connectOverCDP(endpointURL, params = {}) {
if (this.name() !== "chromium")
throw new Error("Connecting over CDP is only supported in Chromium.");
const headers = params.headers ? (0, import_headers.headersObjectToArray)(params.headers) : void 0;
const result = await this._channel.connectOverCDP({
endpointURL,
headers,
slowMo: params.slowMo,
timeout: new import_timeoutSettings.TimeoutSettings(this._platform).timeout(params),
isLocal: params.isLocal
});
const browser = import_browser.Browser.from(result.browser);
browser._connectToBrowserType(this, {}, params.logger);
if (result.defaultContext)
await this._instrumentation.runAfterCreateBrowserContext(import_browserContext.BrowserContext.from(result.defaultContext));
return browser;
}
async _connectOverCDPTransport(transport) {
if (this.name() !== "chromium")
throw new Error("Connecting over CDP is only supported in Chromium.");
const result = await this._channel.connectOverCDPTransport({ transport });
const browser = import_browser.Browser.from(result.browser);
browser._connectToBrowserType(this, {}, void 0);
if (result.defaultContext)
await this._instrumentation.runAfterCreateBrowserContext(import_browserContext.BrowserContext.from(result.defaultContext));
return browser;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserType
});
+55
View File
@@ -0,0 +1,55 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var cdpSession_exports = {};
__export(cdpSession_exports, {
CDPSession: () => CDPSession
});
module.exports = __toCommonJS(cdpSession_exports);
var import_channelOwner = require("./channelOwner");
class CDPSession extends import_channelOwner.ChannelOwner {
static from(cdpSession) {
return cdpSession._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._channel.on("event", (event) => {
this.emit(event.method, event.params);
this.emit("event", event);
});
this._channel.on("close", () => {
this.emit("close", this);
});
this.on = super.on;
this.addListener = super.addListener;
this.off = super.removeListener;
this.removeListener = super.removeListener;
this.once = super.once;
}
async send(method, params) {
const result = await this._channel.send({ method, params });
return result.result;
}
async detach() {
return await this._channel.detach();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CDPSession
});
+194
View File
@@ -0,0 +1,194 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var channelOwner_exports = {};
__export(channelOwner_exports, {
ChannelOwner: () => ChannelOwner
});
module.exports = __toCommonJS(channelOwner_exports);
var import_eventEmitter = require("./eventEmitter");
var import_validator = require("../protocol/validator");
var import_protocolMetainfo = require("../utils/isomorphic/protocolMetainfo");
var import_clientStackTrace = require("./clientStackTrace");
var import_stackTrace = require("../utils/isomorphic/stackTrace");
class ChannelOwner extends import_eventEmitter.EventEmitter {
constructor(parent, type, guid, initializer) {
const connection = parent instanceof ChannelOwner ? parent._connection : parent;
super(connection._platform);
this._objects = /* @__PURE__ */ new Map();
this._eventToSubscriptionMapping = /* @__PURE__ */ new Map();
this._wasCollected = false;
this.setMaxListeners(0);
this._connection = connection;
this._type = type;
this._guid = guid;
this._parent = parent instanceof ChannelOwner ? parent : void 0;
this._instrumentation = this._connection._instrumentation;
this._connection._objects.set(guid, this);
if (this._parent) {
this._parent._objects.set(guid, this);
this._logger = this._parent._logger;
}
this._channel = this._createChannel(new import_eventEmitter.EventEmitter(connection._platform));
this._initializer = initializer;
}
_setEventToSubscriptionMapping(mapping) {
this._eventToSubscriptionMapping = mapping;
}
_updateSubscription(event, enabled) {
const protocolEvent = this._eventToSubscriptionMapping.get(String(event));
if (protocolEvent)
this._channel.updateSubscription({ event: protocolEvent, enabled }).catch(() => {
});
}
on(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.on(event, listener);
return this;
}
addListener(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.addListener(event, listener);
return this;
}
prependListener(event, listener) {
if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.prependListener(event, listener);
return this;
}
off(event, listener) {
super.off(event, listener);
if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this;
}
removeListener(event, listener) {
super.removeListener(event, listener);
if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this;
}
_adopt(child) {
child._parent._objects.delete(child._guid);
this._objects.set(child._guid, child);
child._parent = this;
}
_dispose(reason) {
if (this._parent)
this._parent._objects.delete(this._guid);
this._connection._objects.delete(this._guid);
this._wasCollected = reason === "gc";
for (const object of [...this._objects.values()])
object._dispose(reason);
this._objects.clear();
}
_debugScopeState() {
return {
_guid: this._guid,
objects: Array.from(this._objects.values()).map((o) => o._debugScopeState())
};
}
_validatorToWireContext() {
return {
tChannelImpl: tChannelImplToWire,
binary: this._connection.rawBuffers() ? "buffer" : "toBase64",
isUnderTest: () => this._platform.isUnderTest()
};
}
_createChannel(base) {
const channel = new Proxy(base, {
get: (obj, prop) => {
if (typeof prop === "string") {
const validator = (0, import_validator.maybeFindValidator)(this._type, prop, "Params");
const { internal } = (0, import_protocolMetainfo.getMetainfo)({ type: this._type, method: prop }) || {};
if (validator) {
return async (params) => {
return await this._wrapApiCall(async (apiZone) => {
const validatedParams = validator(params, "", this._validatorToWireContext());
if (!apiZone.internal && !apiZone.reported) {
apiZone.reported = true;
this._instrumentation.onApiCallBegin(apiZone, { type: this._type, method: prop, params });
logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`);
return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone);
}
return await this._connection.sendMessageToServer(this, prop, validatedParams, { internal: true });
}, { internal });
};
}
}
return obj[prop];
}
});
channel._object = this;
return channel;
}
async _wrapApiCall(func, options) {
const logger = this._logger;
const existingApiZone = this._platform.zones.current().data();
if (existingApiZone)
return await func(existingApiZone);
const stackTrace = (0, import_clientStackTrace.captureLibraryStackTrace)(this._platform);
const apiZone = { title: options?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options?.internal ?? false, reported: false, userData: void 0, stepId: void 0 };
try {
const result = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone));
if (!options?.internal) {
logApiCall(this._platform, logger, `<= ${apiZone.apiName} succeeded`);
this._instrumentation.onApiCallEnd(apiZone);
}
return result;
} catch (e) {
const innerError = (this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack ? "\n<inner error>\n" + e.stack : "";
if (apiZone.apiName && !apiZone.apiName.includes("<anonymous>"))
e.message = apiZone.apiName + ": " + e.message;
const stackFrames = "\n" + (0, import_stackTrace.stringifyStackFrames)(stackTrace.frames).join("\n") + innerError;
if (stackFrames.trim())
e.stack = e.message + stackFrames;
else
e.stack = "";
if (!options?.internal) {
apiZone.error = e;
logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`);
this._instrumentation.onApiCallEnd(apiZone);
}
throw e;
}
}
toJSON() {
return {
_type: this._type,
_guid: this._guid
};
}
}
function logApiCall(platform, logger, message) {
if (logger && logger.isEnabled("api", "info"))
logger.log("api", "info", message, [], { color: "cyan" });
platform.log("api", message);
}
function tChannelImplToWire(names, arg, path, context) {
if (arg._object instanceof ChannelOwner && (names === "*" || names.includes(arg._object._type)))
return { guid: arg._object._guid };
throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ChannelOwner
});
+64
View File
@@ -0,0 +1,64 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clientHelper_exports = {};
__export(clientHelper_exports, {
addSourceUrlToScript: () => addSourceUrlToScript,
envObjectToArray: () => envObjectToArray,
evaluationScript: () => evaluationScript
});
module.exports = __toCommonJS(clientHelper_exports);
var import_rtti = require("../utils/isomorphic/rtti");
function envObjectToArray(env) {
const result = [];
for (const name in env) {
if (!Object.is(env[name], void 0))
result.push({ name, value: String(env[name]) });
}
return result;
}
async function evaluationScript(platform, fun, arg, addSourceUrl = true) {
if (typeof fun === "function") {
const source = fun.toString();
const argString = Object.is(arg, void 0) ? "undefined" : JSON.stringify(arg);
return `(${source})(${argString})`;
}
if (arg !== void 0)
throw new Error("Cannot evaluate a string with arguments");
if ((0, import_rtti.isString)(fun))
return fun;
if (fun.content !== void 0)
return fun.content;
if (fun.path !== void 0) {
let source = await platform.fs().promises.readFile(fun.path, "utf8");
if (addSourceUrl)
source = addSourceUrlToScript(source, fun.path);
return source;
}
throw new Error("Either path or content property must be present");
}
function addSourceUrlToScript(source, path) {
return `${source}
//# sourceURL=${path.replace(/\n/g, "")}`;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
addSourceUrlToScript,
envObjectToArray,
evaluationScript
});
+55
View File
@@ -0,0 +1,55 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clientInstrumentation_exports = {};
__export(clientInstrumentation_exports, {
createInstrumentation: () => createInstrumentation
});
module.exports = __toCommonJS(clientInstrumentation_exports);
function createInstrumentation() {
const listeners = [];
return new Proxy({}, {
get: (obj, prop) => {
if (typeof prop !== "string")
return obj[prop];
if (prop === "addListener")
return (listener) => listeners.push(listener);
if (prop === "removeListener")
return (listener) => listeners.splice(listeners.indexOf(listener), 1);
if (prop === "removeAllListeners")
return () => listeners.splice(0, listeners.length);
if (prop.startsWith("run")) {
return async (...params) => {
for (const listener of listeners)
await listener[prop]?.(...params);
};
}
if (prop.startsWith("on")) {
return (...params) => {
for (const listener of listeners)
listener[prop]?.(...params);
};
}
return obj[prop];
}
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createInstrumentation
});
+69
View File
@@ -0,0 +1,69 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clientStackTrace_exports = {};
__export(clientStackTrace_exports, {
captureLibraryStackTrace: () => captureLibraryStackTrace
});
module.exports = __toCommonJS(clientStackTrace_exports);
var import_stackTrace = require("../utils/isomorphic/stackTrace");
function captureLibraryStackTrace(platform) {
const stack = (0, import_stackTrace.captureRawStack)();
let parsedFrames = stack.map((line) => {
const frame = (0, import_stackTrace.parseStackFrame)(line, platform.pathSeparator, platform.showInternalStackFrames());
if (!frame || !frame.file)
return null;
const isPlaywrightLibrary = !!platform.coreDir && frame.file.startsWith(platform.coreDir);
const parsed = {
frame,
frameText: line,
isPlaywrightLibrary
};
return parsed;
}).filter(Boolean);
let apiName = "";
for (let i = 0; i < parsedFrames.length - 1; i++) {
const parsedFrame = parsedFrames[i];
if (parsedFrame.isPlaywrightLibrary && !parsedFrames[i + 1].isPlaywrightLibrary) {
apiName = apiName || normalizeAPIName(parsedFrame.frame.function);
break;
}
}
function normalizeAPIName(name) {
if (!name)
return "";
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
if (!match)
return name;
return match[1].toLowerCase() + match[2];
}
const filterPrefixes = platform.boxedStackPrefixes();
parsedFrames = parsedFrames.filter((f) => {
if (filterPrefixes.some((prefix) => f.frame.file.startsWith(prefix)))
return false;
return true;
});
return {
frames: parsedFrames.map((p) => p.frame),
apiName
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
captureLibraryStackTrace
});
+68
View File
@@ -0,0 +1,68 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clock_exports = {};
__export(clock_exports, {
Clock: () => Clock
});
module.exports = __toCommonJS(clock_exports);
class Clock {
constructor(browserContext) {
this._browserContext = browserContext;
}
async install(options = {}) {
await this._browserContext._channel.clockInstall(options.time !== void 0 ? parseTime(options.time) : {});
}
async fastForward(ticks) {
await this._browserContext._channel.clockFastForward(parseTicks(ticks));
}
async pauseAt(time) {
await this._browserContext._channel.clockPauseAt(parseTime(time));
}
async resume() {
await this._browserContext._channel.clockResume({});
}
async runFor(ticks) {
await this._browserContext._channel.clockRunFor(parseTicks(ticks));
}
async setFixedTime(time) {
await this._browserContext._channel.clockSetFixedTime(parseTime(time));
}
async setSystemTime(time) {
await this._browserContext._channel.clockSetSystemTime(parseTime(time));
}
}
function parseTime(time) {
if (typeof time === "number")
return { timeNumber: time };
if (typeof time === "string")
return { timeString: time };
if (!isFinite(time.getTime()))
throw new Error(`Invalid date: ${time}`);
return { timeNumber: time.getTime() };
}
function parseTicks(ticks) {
return {
ticksNumber: typeof ticks === "number" ? ticks : void 0,
ticksString: typeof ticks === "string" ? ticks : void 0
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Clock
});
+143
View File
@@ -0,0 +1,143 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var connect_exports = {};
__export(connect_exports, {
connectToBrowser: () => connectToBrowser,
connectToEndpoint: () => connectToEndpoint
});
module.exports = __toCommonJS(connect_exports);
var import_time = require("../utils/isomorphic/time");
var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
var import_browser = require("./browser");
var import_connection = require("./connection");
var import_events = require("./events");
async function connectToBrowser(playwright, params) {
const deadline = params.timeout ? (0, import_time.monotonicTime)() + params.timeout : 0;
const nameParam = params.browserName ? { "x-playwright-browser": params.browserName } : {};
const headers = { ...nameParam, ...params.headers };
const connectParams = {
endpoint: params.endpoint,
headers,
exposeNetwork: params.exposeNetwork,
slowMo: params.slowMo,
timeout: params.timeout || 0
};
if (params.__testHookRedirectPortForwarding)
connectParams.socksProxyRedirectPortForTest = params.__testHookRedirectPortForwarding;
const connection = await connectToEndpoint(playwright._connection, connectParams);
let browser;
connection.on("close", () => {
for (const context of browser?.contexts() || []) {
for (const page of context.pages())
page._onClose();
context._onClose();
}
setTimeout(() => browser?._didClose(), 0);
});
const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
if (params.__testHookBeforeCreateBrowser)
await params.__testHookBeforeCreateBrowser();
const playwright2 = await connection.initializePlaywright();
if (!playwright2._initializer.preLaunchedBrowser) {
connection.close();
throw new Error("Malformed endpoint. Did you use BrowserType.launchServer method?");
}
playwright2.selectors = playwright2.selectors;
browser = import_browser.Browser.from(playwright2._initializer.preLaunchedBrowser);
browser._shouldCloseConnectionOnClose = true;
browser.on(import_events.Events.Browser.Disconnected, () => connection.close());
return browser;
}, deadline);
if (!result.timedOut) {
return result.result;
} else {
connection.close();
throw new Error(`Timeout ${params.timeout}ms exceeded`);
}
}
async function connectToEndpoint(parentConnection, params) {
const localUtils = parentConnection.localUtils();
const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
const connectHeaders = await transport.connect(params);
const connection = new import_connection.Connection(parentConnection._platform, localUtils, parentConnection._instrumentation, connectHeaders);
connection.markAsRemote();
connection.on("close", () => transport.close());
let closeError;
const onTransportClosed = (reason) => {
connection.close(reason || closeError);
};
transport.onClose((reason) => onTransportClosed(reason));
connection.onmessage = (message) => transport.send(message).catch(() => onTransportClosed());
transport.onMessage((message) => {
try {
connection.dispatch(message);
} catch (e) {
closeError = String(e);
transport.close().catch(() => {
});
}
});
return connection;
}
class JsonPipeTransport {
constructor(owner) {
this._owner = owner;
}
async connect(params) {
const { pipe, headers: connectHeaders } = await this._owner._channel.connect(params);
this._pipe = pipe;
return connectHeaders;
}
async send(message) {
await this._pipe.send({ message });
}
onMessage(callback) {
this._pipe.on("message", ({ message }) => callback(message));
}
onClose(callback) {
this._pipe.on("closed", ({ reason }) => callback(reason));
}
async close() {
await this._pipe.close().catch(() => {
});
}
}
class WebSocketTransport {
async connect(params) {
this._ws = new window.WebSocket(params.endpoint);
return [];
}
async send(message) {
this._ws.send(JSON.stringify(message));
}
onMessage(callback) {
this._ws.addEventListener("message", (event) => callback(JSON.parse(event.data)));
}
onClose(callback) {
this._ws.addEventListener("close", () => callback());
}
async close() {
this._ws.close();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
connectToBrowser,
connectToEndpoint
});
+322
View File
@@ -0,0 +1,322 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var connection_exports = {};
__export(connection_exports, {
Connection: () => Connection
});
module.exports = __toCommonJS(connection_exports);
var import_eventEmitter = require("./eventEmitter");
var import_android = require("./android");
var import_artifact = require("./artifact");
var import_browser = require("./browser");
var import_browserContext = require("./browserContext");
var import_browserType = require("./browserType");
var import_cdpSession = require("./cdpSession");
var import_channelOwner = require("./channelOwner");
var import_clientInstrumentation = require("./clientInstrumentation");
var import_debugger = require("./debugger");
var import_dialog = require("./dialog");
var import_disposable = require("./disposable");
var import_electron = require("./electron");
var import_elementHandle = require("./elementHandle");
var import_errors = require("./errors");
var import_fetch = require("./fetch");
var import_frame = require("./frame");
var import_jsHandle = require("./jsHandle");
var import_jsonPipe = require("./jsonPipe");
var import_localUtils = require("./localUtils");
var import_network = require("./network");
var import_page = require("./page");
var import_playwright = require("./playwright");
var import_stream = require("./stream");
var import_tracing = require("./tracing");
var import_worker = require("./worker");
var import_writableStream = require("./writableStream");
var import_validator = require("../protocol/validator");
var import_stackTrace = require("../utils/isomorphic/stackTrace");
class Root extends import_channelOwner.ChannelOwner {
constructor(connection) {
super(connection, "Root", "", {});
}
async initialize() {
return import_playwright.Playwright.from((await this._channel.initialize({
sdkLanguage: "javascript"
})).playwright);
}
}
class DummyChannelOwner extends import_channelOwner.ChannelOwner {
}
class Connection extends import_eventEmitter.EventEmitter {
constructor(platform, localUtils, instrumentation, headers = []) {
super(platform);
this._objects = /* @__PURE__ */ new Map();
this.onmessage = (message) => {
};
this._lastId = 0;
this._callbacks = /* @__PURE__ */ new Map();
this._isRemote = false;
this._rawBuffers = false;
this._tracingCount = 0;
this._instrumentation = instrumentation || (0, import_clientInstrumentation.createInstrumentation)();
this._localUtils = localUtils;
this._rootObject = new Root(this);
this.headers = headers;
}
markAsRemote() {
this._isRemote = true;
}
isRemote() {
return this._isRemote;
}
useRawBuffers() {
this._rawBuffers = true;
}
rawBuffers() {
return this._rawBuffers;
}
localUtils() {
return this._localUtils;
}
async initializePlaywright() {
return await this._rootObject.initialize();
}
getObjectWithKnownName(guid) {
return this._objects.get(guid);
}
setIsTracing(isTracing) {
if (isTracing)
this._tracingCount++;
else
this._tracingCount--;
}
async sendMessageToServer(object, method, params, options) {
if (this._closedError)
throw this._closedError;
if (object._wasCollected)
throw new Error("The object has been collected to prevent unbounded heap growth.");
const guid = object._guid;
const type = object._type;
const id = ++this._lastId;
const message = { id, guid, method, params };
if (this._platform.isLogEnabled("channel")) {
this._platform.log("channel", "SEND> " + JSON.stringify(message));
}
const location = options.frames?.[0] ? { file: options.frames[0].file, line: options.frames[0].line, column: options.frames[0].column } : void 0;
const metadata = { title: options.title, location, internal: options.internal, stepId: options.stepId };
if (this._tracingCount && options.frames && type !== "LocalUtils")
this._localUtils?.addStackToTracingNoReply({ callData: { stack: options.frames ?? [], id } }).catch(() => {
});
this._platform.zones.empty.run(() => this.onmessage({ ...message, metadata }));
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, title: options.title, type, method }));
}
_validatorFromWireContext() {
return {
tChannelImpl: this._tChannelImplFromWire.bind(this),
binary: this._rawBuffers ? "buffer" : "fromBase64",
isUnderTest: () => this._platform.isUnderTest()
};
}
dispatch(message) {
if (this._closedError)
return;
const { id, guid, method, params, result, error, log } = message;
if (id) {
if (this._platform.isLogEnabled("channel"))
this._platform.log("channel", "<RECV " + JSON.stringify(message));
const callback = this._callbacks.get(id);
if (!callback)
throw new Error(`Cannot find command to respond: ${id}`);
this._callbacks.delete(id);
if (error && !result) {
const parsedError = (0, import_errors.parseError)(error);
(0, import_stackTrace.rewriteErrorMessage)(parsedError, parsedError.message + formatCallLog(this._platform, log));
callback.reject(parsedError);
} else {
const validator2 = (0, import_validator.findValidator)(callback.type, callback.method, "Result");
callback.resolve(validator2(result, "", this._validatorFromWireContext()));
}
return;
}
if (this._platform.isLogEnabled("channel"))
this._platform.log("channel", "<EVENT " + JSON.stringify(message));
if (method === "__create__") {
this._createRemoteObject(guid, params.type, params.guid, params.initializer);
return;
}
const object = this._objects.get(guid);
if (!object)
throw new Error(`Cannot find object to "${method}": ${guid}`);
if (method === "__adopt__") {
const child = this._objects.get(params.guid);
if (!child)
throw new Error(`Unknown new child: ${params.guid}`);
object._adopt(child);
return;
}
if (method === "__dispose__") {
object._dispose(params.reason);
return;
}
const validator = (0, import_validator.findValidator)(object._type, method, "Event");
object._channel.emit(method, validator(params, "", this._validatorFromWireContext()));
}
close(cause) {
if (this._closedError)
return;
this._closedError = new import_errors.TargetClosedError(cause);
for (const callback of this._callbacks.values())
callback.reject(this._closedError);
this._callbacks.clear();
this.emit("close");
}
_tChannelImplFromWire(names, arg, path, context) {
if (arg && typeof arg === "object" && typeof arg.guid === "string") {
const object = this._objects.get(arg.guid);
if (!object)
throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
if (names !== "*" && !names.includes(object._type))
throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
return object._channel;
}
throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
}
_createRemoteObject(parentGuid, type, guid, initializer) {
const parent = this._objects.get(parentGuid);
if (!parent)
throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
let result;
const validator = (0, import_validator.findValidator)(type, "", "Initializer");
initializer = validator(initializer, "", this._validatorFromWireContext());
switch (type) {
case "Android":
result = new import_android.Android(parent, type, guid, initializer);
break;
case "AndroidSocket":
result = new import_android.AndroidSocket(parent, type, guid, initializer);
break;
case "AndroidDevice":
result = new import_android.AndroidDevice(parent, type, guid, initializer);
break;
case "APIRequestContext":
result = new import_fetch.APIRequestContext(parent, type, guid, initializer);
break;
case "Artifact":
result = new import_artifact.Artifact(parent, type, guid, initializer);
break;
case "BindingCall":
result = new import_page.BindingCall(parent, type, guid, initializer);
break;
case "Browser":
result = new import_browser.Browser(parent, type, guid, initializer);
break;
case "BrowserContext":
result = new import_browserContext.BrowserContext(parent, type, guid, initializer);
break;
case "BrowserType":
result = new import_browserType.BrowserType(parent, type, guid, initializer);
break;
case "CDPSession":
result = new import_cdpSession.CDPSession(parent, type, guid, initializer);
break;
case "Debugger":
result = new import_debugger.Debugger(parent, type, guid, initializer);
break;
case "Dialog":
result = new import_dialog.Dialog(parent, type, guid, initializer);
break;
case "Disposable":
result = new import_disposable.DisposableObject(parent, type, guid, initializer);
break;
case "Electron":
result = new import_electron.Electron(parent, type, guid, initializer);
break;
case "ElectronApplication":
result = new import_electron.ElectronApplication(parent, type, guid, initializer);
break;
case "ElementHandle":
result = new import_elementHandle.ElementHandle(parent, type, guid, initializer);
break;
case "Frame":
result = new import_frame.Frame(parent, type, guid, initializer);
break;
case "JSHandle":
result = new import_jsHandle.JSHandle(parent, type, guid, initializer);
break;
case "JsonPipe":
result = new import_jsonPipe.JsonPipe(parent, type, guid, initializer);
break;
case "LocalUtils":
result = new import_localUtils.LocalUtils(parent, type, guid, initializer);
if (!this._localUtils)
this._localUtils = result;
break;
case "Page":
result = new import_page.Page(parent, type, guid, initializer);
break;
case "Playwright":
result = new import_playwright.Playwright(parent, type, guid, initializer);
break;
case "Request":
result = new import_network.Request(parent, type, guid, initializer);
break;
case "Response":
result = new import_network.Response(parent, type, guid, initializer);
break;
case "Route":
result = new import_network.Route(parent, type, guid, initializer);
break;
case "Stream":
result = new import_stream.Stream(parent, type, guid, initializer);
break;
case "SocksSupport":
result = new DummyChannelOwner(parent, type, guid, initializer);
break;
case "Tracing":
result = new import_tracing.Tracing(parent, type, guid, initializer);
break;
case "WebSocket":
result = new import_network.WebSocket(parent, type, guid, initializer);
break;
case "WebSocketRoute":
result = new import_network.WebSocketRoute(parent, type, guid, initializer);
break;
case "Worker":
result = new import_worker.Worker(parent, type, guid, initializer);
break;
case "WritableStream":
result = new import_writableStream.WritableStream(parent, type, guid, initializer);
break;
default:
throw new Error("Missing type " + type);
}
return result;
}
}
function formatCallLog(platform, log) {
if (!log || !log.some((l) => !!l))
return "";
return `
Call log:
${platform.colors.dim(log.join("\n"))}
`;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Connection
});
+61
View File
@@ -0,0 +1,61 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var consoleMessage_exports = {};
__export(consoleMessage_exports, {
ConsoleMessage: () => ConsoleMessage
});
module.exports = __toCommonJS(consoleMessage_exports);
var import_jsHandle = require("./jsHandle");
class ConsoleMessage {
constructor(platform, event, page, worker) {
this._page = page;
this._worker = worker;
this._event = event;
if (platform.inspectCustom)
this[platform.inspectCustom] = () => this._inspect();
}
worker() {
return this._worker;
}
page() {
return this._page;
}
type() {
return this._event.type;
}
text() {
return this._event.text;
}
args() {
return this._event.args.map(import_jsHandle.JSHandle.from);
}
location() {
return this._event.location;
}
timestamp() {
return this._event.timestamp;
}
_inspect() {
return this.text();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ConsoleMessage
});
+44
View File
@@ -0,0 +1,44 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var coverage_exports = {};
__export(coverage_exports, {
Coverage: () => Coverage
});
module.exports = __toCommonJS(coverage_exports);
class Coverage {
constructor(channel) {
this._channel = channel;
}
async startJSCoverage(options = {}) {
await this._channel.startJSCoverage(options);
}
async stopJSCoverage() {
return (await this._channel.stopJSCoverage()).entries;
}
async startCSSCoverage(options = {}) {
await this._channel.startCSSCoverage(options);
}
async stopCSSCoverage() {
return (await this._channel.stopCSSCoverage()).entries;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Coverage
});
+57
View File
@@ -0,0 +1,57 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var debugger_exports = {};
__export(debugger_exports, {
Debugger: () => Debugger
});
module.exports = __toCommonJS(debugger_exports);
var import_channelOwner = require("./channelOwner");
var import_events = require("./events");
class Debugger extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._pausedDetails = null;
this._channel.on("pausedStateChanged", ({ pausedDetails }) => {
this._pausedDetails = pausedDetails ?? null;
this.emit(import_events.Events.Debugger.PausedStateChanged);
});
}
static from(channel) {
return channel._object;
}
async requestPause() {
await this._channel.requestPause();
}
async resume() {
await this._channel.resume();
}
async next() {
await this._channel.next();
}
async runTo(location) {
await this._channel.runTo({ location });
}
pausedDetails() {
return this._pausedDetails;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Debugger
});
+63
View File
@@ -0,0 +1,63 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var dialog_exports = {};
__export(dialog_exports, {
Dialog: () => Dialog
});
module.exports = __toCommonJS(dialog_exports);
var import_channelOwner = require("./channelOwner");
var import_page = require("./page");
var import_errors = require("./errors");
class Dialog extends import_channelOwner.ChannelOwner {
static from(dialog) {
return dialog._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._page = import_page.Page.fromNullable(initializer.page);
}
page() {
return this._page;
}
type() {
return this._initializer.type;
}
message() {
return this._initializer.message;
}
defaultValue() {
return this._initializer.defaultValue;
}
async accept(promptText) {
await this._channel.accept({ promptText });
}
async dismiss() {
try {
await this._channel.dismiss();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Dialog
});
+76
View File
@@ -0,0 +1,76 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var disposable_exports = {};
__export(disposable_exports, {
DisposableObject: () => DisposableObject,
DisposableStub: () => DisposableStub,
disposeAll: () => disposeAll
});
module.exports = __toCommonJS(disposable_exports);
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
class DisposableObject extends import_channelOwner.ChannelOwner {
static from(channel) {
return channel._object;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
try {
await this._channel.dispose();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
}
class DisposableStub {
constructor(dispose) {
this._dispose = dispose;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
if (!this._dispose)
return;
try {
const dispose = this._dispose;
this._dispose = void 0;
await dispose();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
}
async function disposeAll(disposables) {
const copy = [...disposables];
disposables.length = 0;
await Promise.all(copy.map((d) => d.dispose()));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DisposableObject,
DisposableStub,
disposeAll
});
+62
View File
@@ -0,0 +1,62 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var download_exports = {};
__export(download_exports, {
Download: () => Download
});
module.exports = __toCommonJS(download_exports);
class Download {
constructor(page, url, suggestedFilename, artifact) {
this._page = page;
this._url = url;
this._suggestedFilename = suggestedFilename;
this._artifact = artifact;
}
page() {
return this._page;
}
url() {
return this._url;
}
suggestedFilename() {
return this._suggestedFilename;
}
async path() {
return await this._artifact.pathAfterFinished();
}
async saveAs(path) {
return await this._artifact.saveAs(path);
}
async failure() {
return await this._artifact.failure();
}
async createReadStream() {
return await this._artifact.createReadStream();
}
async cancel() {
return await this._artifact.cancel();
}
async delete() {
return await this._artifact.delete();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Download
});
+139
View File
@@ -0,0 +1,139 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var electron_exports = {};
__export(electron_exports, {
Electron: () => Electron,
ElectronApplication: () => ElectronApplication
});
module.exports = __toCommonJS(electron_exports);
var import_browserContext = require("./browserContext");
var import_channelOwner = require("./channelOwner");
var import_clientHelper = require("./clientHelper");
var import_consoleMessage = require("./consoleMessage");
var import_errors = require("./errors");
var import_events = require("./events");
var import_jsHandle = require("./jsHandle");
var import_waiter = require("./waiter");
var import_timeoutSettings = require("./timeoutSettings");
class Electron extends import_channelOwner.ChannelOwner {
static from(electron) {
return electron._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
}
async launch(options = {}) {
options = this._playwright.selectors._withSelectorOptions(options);
const params = {
...await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options),
env: (0, import_clientHelper.envObjectToArray)(options.env ? options.env : this._platform.env),
tracesDir: options.tracesDir,
artifactsDir: options.artifactsDir,
timeout: new import_timeoutSettings.TimeoutSettings(this._platform).launchTimeout(options)
};
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
this._playwright.selectors._contextsForSelectors.add(app._context);
app.once(import_events.Events.ElectronApplication.Close, () => this._playwright.selectors._contextsForSelectors.delete(app._context));
await app._context._initializeHarFromOptions(options.recordHar);
app._context.tracing._tracesDir = options.tracesDir;
return app;
}
}
class ElectronApplication extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._windows = /* @__PURE__ */ new Set();
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
this._context = import_browserContext.BrowserContext.from(initializer.context);
for (const page of this._context._pages)
this._onPage(page);
this._context.on(import_events.Events.BrowserContext.Page, (page) => this._onPage(page));
this._channel.on("close", () => {
this.emit(import_events.Events.ElectronApplication.Close);
});
this._channel.on("console", (event) => this.emit(import_events.Events.ElectronApplication.Console, new import_consoleMessage.ConsoleMessage(this._platform, event, null, null)));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.ElectronApplication.Console, "console"]
]));
}
static from(electronApplication) {
return electronApplication._object;
}
process() {
return this._connection.toImpl?.(this)?.process();
}
_onPage(page) {
this._windows.add(page);
this.emit(import_events.Events.ElectronApplication.Window, page);
page.once(import_events.Events.Page.Close, () => this._windows.delete(page));
}
windows() {
return [...this._windows];
}
async firstWindow(options) {
if (this._windows.size)
return this._windows.values().next().value;
return await this.waitForEvent("window", options);
}
context() {
return this._context;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close() {
try {
await this._context.close();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== import_events.Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, import_events.Events.ElectronApplication.Close, () => new import_errors.TargetClosedError());
const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result;
});
}
async browserWindow(page) {
const result = await this._channel.browserWindow({ page: page._channel });
return import_jsHandle.JSHandle.from(result.handle);
}
async evaluate(pageFunction, arg) {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async evaluateHandle(pageFunction, arg) {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return import_jsHandle.JSHandle.from(result.handle);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Electron,
ElectronApplication
});
+281
View File
@@ -0,0 +1,281 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var elementHandle_exports = {};
__export(elementHandle_exports, {
ElementHandle: () => ElementHandle,
convertInputFiles: () => convertInputFiles,
convertSelectOptionValues: () => convertSelectOptionValues,
determineScreenshotType: () => determineScreenshotType
});
module.exports = __toCommonJS(elementHandle_exports);
var import_frame = require("./frame");
var import_jsHandle = require("./jsHandle");
var import_assert = require("../utils/isomorphic/assert");
var import_fileUtils = require("./fileUtils");
var import_rtti = require("../utils/isomorphic/rtti");
var import_writableStream = require("./writableStream");
var import_mimeType = require("../utils/isomorphic/mimeType");
class ElementHandle extends import_jsHandle.JSHandle {
static from(handle) {
return handle._object;
}
static fromNullable(handle) {
return handle ? ElementHandle.from(handle) : null;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._frame = parent;
this._elementChannel = this._channel;
}
asElement() {
return this;
}
async ownerFrame() {
return import_frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
}
async contentFrame() {
return import_frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
}
async getAttribute(name) {
const value = (await this._elementChannel.getAttribute({ name })).value;
return value === void 0 ? null : value;
}
async inputValue() {
return (await this._elementChannel.inputValue()).value;
}
async textContent() {
const value = (await this._elementChannel.textContent()).value;
return value === void 0 ? null : value;
}
async innerText() {
return (await this._elementChannel.innerText()).value;
}
async innerHTML() {
return (await this._elementChannel.innerHTML()).value;
}
async isChecked() {
return (await this._elementChannel.isChecked()).value;
}
async isDisabled() {
return (await this._elementChannel.isDisabled()).value;
}
async isEditable() {
return (await this._elementChannel.isEditable()).value;
}
async isEnabled() {
return (await this._elementChannel.isEnabled()).value;
}
async isHidden() {
return (await this._elementChannel.isHidden()).value;
}
async isVisible() {
return (await this._elementChannel.isVisible()).value;
}
async dispatchEvent(type, eventInit = {}) {
await this._elementChannel.dispatchEvent({ type, eventInit: (0, import_jsHandle.serializeArgument)(eventInit) });
}
async scrollIntoViewIfNeeded(options = {}) {
await this._elementChannel.scrollIntoViewIfNeeded({ ...options, timeout: this._frame._timeout(options) });
}
async hover(options = {}) {
await this._elementChannel.hover({ ...options, timeout: this._frame._timeout(options) });
}
async click(options = {}) {
return await this._elementChannel.click({ ...options, timeout: this._frame._timeout(options) });
}
async dblclick(options = {}) {
return await this._elementChannel.dblclick({ ...options, timeout: this._frame._timeout(options) });
}
async tap(options = {}) {
return await this._elementChannel.tap({ ...options, timeout: this._frame._timeout(options) });
}
async selectOption(values, options = {}) {
const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options, timeout: this._frame._timeout(options) });
return result.values;
}
async fill(value, options = {}) {
return await this._elementChannel.fill({ value, ...options, timeout: this._frame._timeout(options) });
}
async selectText(options = {}) {
await this._elementChannel.selectText({ ...options, timeout: this._frame._timeout(options) });
}
async setInputFiles(files, options = {}) {
const frame = await this.ownerFrame();
if (!frame)
throw new Error("Cannot set input files to detached element");
const converted = await convertInputFiles(this._platform, files, frame.page().context());
await this._elementChannel.setInputFiles({ ...converted, ...options, timeout: this._frame._timeout(options) });
}
async focus() {
await this._elementChannel.focus();
}
async type(text, options = {}) {
await this._elementChannel.type({ text, ...options, timeout: this._frame._timeout(options) });
}
async press(key, options = {}) {
await this._elementChannel.press({ key, ...options, timeout: this._frame._timeout(options) });
}
async check(options = {}) {
return await this._elementChannel.check({ ...options, timeout: this._frame._timeout(options) });
}
async uncheck(options = {}) {
return await this._elementChannel.uncheck({ ...options, timeout: this._frame._timeout(options) });
}
async setChecked(checked, options) {
if (checked)
await this.check(options);
else
await this.uncheck(options);
}
async boundingBox() {
const value = (await this._elementChannel.boundingBox()).value;
return value === void 0 ? null : value;
}
async screenshot(options = {}) {
const mask = options.mask;
const copy = { ...options, mask: void 0, timeout: this._frame._timeout(options) };
if (!copy.type)
copy.type = determineScreenshotType(options);
if (mask) {
copy.mask = mask.map((locator) => ({
frame: locator._frame._channel,
selector: locator._selector
}));
}
const result = await this._elementChannel.screenshot(copy);
if (options.path) {
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, result.binary);
}
return result.binary;
}
async $(selector) {
return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element);
}
async $$(selector) {
const result = await this._elementChannel.querySelectorAll({ selector });
return result.elements.map((h) => ElementHandle.from(h));
}
async $eval(selector, pageFunction, arg) {
const result = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async $$eval(selector, pageFunction, arg) {
const result = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async waitForElementState(state, options = {}) {
return await this._elementChannel.waitForElementState({ state, ...options, timeout: this._frame._timeout(options) });
}
async waitForSelector(selector, options = {}) {
const result = await this._elementChannel.waitForSelector({ selector, ...options, timeout: this._frame._timeout(options) });
return ElementHandle.fromNullable(result.element);
}
}
function convertSelectOptionValues(values) {
if (values === null)
return {};
if (!Array.isArray(values))
values = [values];
if (!values.length)
return {};
for (let i = 0; i < values.length; i++)
(0, import_assert.assert)(values[i] !== null, `options[${i}]: expected object, got null`);
if (values[0] instanceof ElementHandle)
return { elements: values.map((v) => v._elementChannel) };
if ((0, import_rtti.isString)(values[0]))
return { options: values.map((valueOrLabel) => ({ valueOrLabel })) };
return { options: values };
}
function filePayloadExceedsSizeLimit(payloads) {
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= import_fileUtils.fileUploadSizeLimit;
}
async function resolvePathsAndDirectoryForInputFiles(platform, items) {
let localPaths;
let localDirectory;
for (const item of items) {
const stat = await platform.fs().promises.stat(item);
if (stat.isDirectory()) {
if (localDirectory)
throw new Error("Multiple directories are not supported");
localDirectory = platform.path().resolve(item);
} else {
localPaths ??= [];
localPaths.push(platform.path().resolve(item));
}
}
if (localPaths?.length && localDirectory)
throw new Error("File paths must be all files or a single directory");
return [localPaths, localDirectory];
}
async function convertInputFiles(platform, files, context) {
const items = Array.isArray(files) ? files.slice() : [files];
if (items.some((item) => typeof item === "string")) {
if (!items.every((item) => typeof item === "string"))
throw new Error("File paths cannot be mixed with buffers");
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items);
if (context._connection.isRemote()) {
const files2 = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter((f) => f.isFile()).map((f) => platform.path().join(f.parentPath, f.name)) : localPaths;
const { writableStreams, rootDir } = await context._wrapApiCall(async () => context._channel.createTempFiles({
rootDirName: localDirectory ? platform.path().basename(localDirectory) : void 0,
items: await Promise.all(files2.map(async (file) => {
const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs;
return {
name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file),
lastModifiedMs
};
}))
}), { internal: true });
for (let i = 0; i < files2.length; i++) {
const writable = import_writableStream.WritableStream.from(writableStreams[i]);
await platform.streamFile(files2[i], writable.stream());
}
return {
directoryStream: rootDir,
streams: localDirectory ? void 0 : writableStreams
};
}
return {
localPaths,
localDirectory
};
}
const payloads = items;
if (filePayloadExceedsSizeLimit(payloads))
throw new Error("Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.");
return { payloads };
}
function determineScreenshotType(options) {
if (options.path) {
const mimeType = (0, import_mimeType.getMimeTypeForPath)(options.path);
if (mimeType === "image/png")
return "png";
else if (mimeType === "image/jpeg")
return "jpeg";
throw new Error(`path: unsupported mime type "${mimeType}"`);
}
return options.type;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ElementHandle,
convertInputFiles,
convertSelectOptionValues,
determineScreenshotType
});
+77
View File
@@ -0,0 +1,77 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var errors_exports = {};
__export(errors_exports, {
TargetClosedError: () => TargetClosedError,
TimeoutError: () => TimeoutError,
isTargetClosedError: () => isTargetClosedError,
parseError: () => parseError,
serializeError: () => serializeError
});
module.exports = __toCommonJS(errors_exports);
var import_serializers = require("../protocol/serializers");
var import_rtti = require("../utils/isomorphic/rtti");
class TimeoutError extends Error {
constructor(message) {
super(message);
this.name = "TimeoutError";
}
}
class TargetClosedError extends Error {
constructor(cause) {
super(cause || "Target page, context or browser has been closed");
}
}
function isTargetClosedError(error) {
return error instanceof TargetClosedError;
}
function serializeError(e) {
if ((0, import_rtti.isError)(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: (0, import_serializers.serializeValue)(e, (value) => ({ fallThrough: value })) };
}
function parseError(error) {
if (!error.error) {
if (error.value === void 0)
throw new Error("Serialized error must have either an error or a value");
return (0, import_serializers.parseSerializedValue)(error.value, void 0);
}
if (error.error.name === "TimeoutError") {
const e2 = new TimeoutError(error.error.message);
e2.stack = error.error.stack || "";
return e2;
}
if (error.error.name === "TargetClosedError") {
const e2 = new TargetClosedError(error.error.message);
e2.stack = error.error.stack || "";
return e2;
}
const e = new Error(error.error.message);
e.stack = error.error.stack || "";
e.name = error.error.name;
return e;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
TargetClosedError,
TimeoutError,
isTargetClosedError,
parseError,
serializeError
});
+314
View File
@@ -0,0 +1,314 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var eventEmitter_exports = {};
__export(eventEmitter_exports, {
EventEmitter: () => EventEmitter
});
module.exports = __toCommonJS(eventEmitter_exports);
class EventEmitter {
constructor(platform) {
this._events = void 0;
this._eventsCount = 0;
this._maxListeners = void 0;
this._pendingHandlers = /* @__PURE__ */ new Map();
this._platform = platform;
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || void 0;
this.on = this.addListener;
this.off = this.removeListener;
}
setMaxListeners(n) {
if (typeof n !== "number" || n < 0 || Number.isNaN(n))
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
this._maxListeners = n;
return this;
}
getMaxListeners() {
return this._maxListeners === void 0 ? this._platform.defaultMaxListeners() : this._maxListeners;
}
emit(type, ...args) {
const events = this._events;
if (events === void 0)
return false;
const handler = events?.[type];
if (handler === void 0)
return false;
if (typeof handler === "function") {
this._callHandler(type, handler, args);
} else {
const len = handler.length;
const listeners = handler.slice();
for (let i = 0; i < len; ++i)
this._callHandler(type, listeners[i], args);
}
return true;
}
_callHandler(type, handler, args) {
const promise = Reflect.apply(handler, this, args);
if (!(promise instanceof Promise))
return;
let set = this._pendingHandlers.get(type);
if (!set) {
set = /* @__PURE__ */ new Set();
this._pendingHandlers.set(type, set);
}
set.add(promise);
promise.catch((e) => {
if (this._rejectionHandler)
this._rejectionHandler(e);
else
throw e;
}).finally(() => set.delete(promise));
}
addListener(type, listener) {
return this._addListener(type, listener, false);
}
on(type, listener) {
return this._addListener(type, listener, false);
}
_addListener(type, listener, prepend) {
checkListener(listener);
let events = this._events;
let existing;
if (events === void 0) {
events = this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
} else {
if (events.newListener !== void 0) {
this.emit("newListener", type, unwrapListener(listener));
events = this._events;
}
existing = events[type];
}
if (existing === void 0) {
existing = events[type] = listener;
++this._eventsCount;
} else {
if (typeof existing === "function") {
existing = events[type] = prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
const m = this.getMaxListeners();
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
const w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
w.name = "MaxListenersExceededWarning";
w.emitter = this;
w.type = type;
w.count = existing.length;
if (!this._platform.isUnderTest()) {
console.warn(w);
}
}
}
return this;
}
prependListener(type, listener) {
return this._addListener(type, listener, true);
}
once(type, listener) {
checkListener(listener);
this.on(type, new OnceWrapper(this, type, listener).wrapperFunction);
return this;
}
prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, new OnceWrapper(this, type, listener).wrapperFunction);
return this;
}
removeListener(type, listener) {
checkListener(listener);
const events = this._events;
if (events === void 0)
return this;
const list = events[type];
if (list === void 0)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) {
this._events = /* @__PURE__ */ Object.create(null);
} else {
delete events[type];
if (events.removeListener)
this.emit("removeListener", type, list.listener ?? listener);
}
} else if (typeof list !== "function") {
let position = -1;
let originalListener;
for (let i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || wrappedListener(list[i]) === listener) {
originalListener = wrappedListener(list[i]);
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else
list.splice(position, 1);
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== void 0)
this.emit("removeListener", type, originalListener || listener);
}
return this;
}
off(type, listener) {
return this.removeListener(type, listener);
}
removeAllListeners(type, options) {
this._removeAllListeners(type);
if (!options)
return this;
if (options.behavior === "wait") {
const errors = [];
this._rejectionHandler = (error) => errors.push(error);
return this._waitFor(type).then(() => {
if (errors.length)
throw errors[0];
});
}
if (options.behavior === "ignoreErrors")
this._rejectionHandler = () => {
};
return Promise.resolve();
}
_removeAllListeners(type) {
const events = this._events;
if (!events)
return;
if (!events.removeListener) {
if (type === void 0) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== void 0) {
if (--this._eventsCount === 0)
this._events = /* @__PURE__ */ Object.create(null);
else
delete events[type];
}
return;
}
if (type === void 0) {
const keys = Object.keys(events);
let key;
for (let i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === "removeListener")
continue;
this._removeAllListeners(key);
}
this._removeAllListeners("removeListener");
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
return;
}
const listeners = events[type];
if (typeof listeners === "function") {
this.removeListener(type, listeners);
} else if (listeners !== void 0) {
for (let i = listeners.length - 1; i >= 0; i--)
this.removeListener(type, listeners[i]);
}
}
listeners(type) {
return this._listeners(this, type, true);
}
rawListeners(type) {
return this._listeners(this, type, false);
}
listenerCount(type) {
const events = this._events;
if (events !== void 0) {
const listener = events[type];
if (typeof listener === "function")
return 1;
if (listener !== void 0)
return listener.length;
}
return 0;
}
eventNames() {
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
}
async _waitFor(type) {
let promises = [];
if (type) {
promises = [...this._pendingHandlers.get(type) || []];
} else {
promises = [];
for (const [, pending] of this._pendingHandlers)
promises.push(...pending);
}
await Promise.all(promises);
}
_listeners(target, type, unwrap) {
const events = target._events;
if (events === void 0)
return [];
const listener = events[type];
if (listener === void 0)
return [];
if (typeof listener === "function")
return unwrap ? [unwrapListener(listener)] : [listener];
return unwrap ? unwrapListeners(listener) : listener.slice();
}
}
function checkListener(listener) {
if (typeof listener !== "function")
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
class OnceWrapper {
constructor(eventEmitter, eventType, listener) {
this._fired = false;
this._eventEmitter = eventEmitter;
this._eventType = eventType;
this._listener = listener;
this.wrapperFunction = this._handle.bind(this);
this.wrapperFunction.listener = listener;
}
_handle(...args) {
if (this._fired)
return;
this._fired = true;
this._eventEmitter.removeListener(this._eventType, this.wrapperFunction);
return this._listener.apply(this._eventEmitter, args);
}
}
function unwrapListener(l) {
return wrappedListener(l) ?? l;
}
function unwrapListeners(arr) {
return arr.map((l) => wrappedListener(l) ?? l);
}
function wrappedListener(l) {
return l.listener;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
EventEmitter
});
+103
View File
@@ -0,0 +1,103 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var events_exports = {};
__export(events_exports, {
Events: () => Events
});
module.exports = __toCommonJS(events_exports);
const Events = {
AndroidDevice: {
WebView: "webview",
Close: "close"
},
AndroidSocket: {
Data: "data",
Close: "close"
},
AndroidWebView: {
Close: "close"
},
Browser: {
Disconnected: "disconnected"
},
Debugger: {
PausedStateChanged: "pausedstatechanged"
},
BrowserContext: {
Console: "console",
Close: "close",
Dialog: "dialog",
Page: "page",
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
WebError: "weberror",
BackgroundPage: "backgroundpage",
// Deprecated in v1.56, never emitted anymore.
ServiceWorker: "serviceworker",
Request: "request",
Response: "response",
RequestFailed: "requestfailed",
RequestFinished: "requestfinished"
},
BrowserServer: {
Close: "close"
},
Page: {
Close: "close",
Crash: "crash",
Console: "console",
Dialog: "dialog",
Download: "download",
FileChooser: "filechooser",
DOMContentLoaded: "domcontentloaded",
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
PageError: "pageerror",
Request: "request",
Response: "response",
RequestFailed: "requestfailed",
RequestFinished: "requestfinished",
FrameAttached: "frameattached",
FrameDetached: "framedetached",
FrameNavigated: "framenavigated",
Load: "load",
Popup: "popup",
WebSocket: "websocket",
Worker: "worker"
},
WebSocket: {
Close: "close",
Error: "socketerror",
FrameReceived: "framereceived",
FrameSent: "framesent"
},
Worker: {
Close: "close",
Console: "console"
},
ElectronApplication: {
Close: "close",
Console: "console",
Window: "window"
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Events
});
+367
View File
@@ -0,0 +1,367 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fetch_exports = {};
__export(fetch_exports, {
APIRequest: () => APIRequest,
APIRequestContext: () => APIRequestContext,
APIResponse: () => APIResponse
});
module.exports = __toCommonJS(fetch_exports);
var import_browserContext = require("./browserContext");
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_network = require("./network");
var import_tracing = require("./tracing");
var import_assert = require("../utils/isomorphic/assert");
var import_fileUtils = require("./fileUtils");
var import_headers = require("../utils/isomorphic/headers");
var import_rtti = require("../utils/isomorphic/rtti");
var import_timeoutSettings = require("./timeoutSettings");
class APIRequest {
constructor(playwright) {
this._contexts = /* @__PURE__ */ new Set();
this._playwright = playwright;
}
async newContext(options = {}) {
options = { ...options };
await this._playwright._instrumentation.runBeforeCreateRequestContext(options);
const storageState = typeof options.storageState === "string" ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, "utf8")) : options.storageState;
const context = APIRequestContext.from((await this._playwright._channel.newRequest({
...options,
extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
storageState,
tracesDir: this._playwright._defaultLaunchOptions?.tracesDir,
// We do not expose tracesDir in the API, so do not allow options to accidentally override it.
clientCertificates: await (0, import_browserContext.toClientCertificatesProtocol)(this._playwright._platform, options.clientCertificates)
})).request);
this._contexts.add(context);
context._request = this;
context._timeoutSettings.setDefaultTimeout(options.timeout ?? this._playwright._defaultContextTimeout);
context._tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir;
await context._instrumentation.runAfterCreateRequestContext(context);
return context;
}
}
class APIRequestContext extends import_channelOwner.ChannelOwner {
static from(channel) {
return channel._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._tracing = import_tracing.Tracing.from(initializer.tracing);
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose(options = {}) {
this._closeReason = options.reason;
await this._instrumentation.runBeforeCloseRequestContext(this);
try {
await this._channel.dispose(options);
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
this._tracing._resetStackCounter();
this._request?._contexts.delete(this);
}
async delete(url, options) {
return await this.fetch(url, {
...options,
method: "DELETE"
});
}
async head(url, options) {
return await this.fetch(url, {
...options,
method: "HEAD"
});
}
async get(url, options) {
return await this.fetch(url, {
...options,
method: "GET"
});
}
async patch(url, options) {
return await this.fetch(url, {
...options,
method: "PATCH"
});
}
async post(url, options) {
return await this.fetch(url, {
...options,
method: "POST"
});
}
async put(url, options) {
return await this.fetch(url, {
...options,
method: "PUT"
});
}
async fetch(urlOrRequest, options = {}) {
const url = (0, import_rtti.isString)(urlOrRequest) ? urlOrRequest : void 0;
const request = (0, import_rtti.isString)(urlOrRequest) ? void 0 : urlOrRequest;
return await this._innerFetch({ url, request, ...options });
}
async _innerFetch(options = {}) {
return await this._wrapApiCall(async () => {
if (this._closeReason)
throw new import_errors.TargetClosedError(this._closeReason);
(0, import_assert.assert)(options.request || typeof options.url === "string", "First argument must be either URL string or Request");
(0, import_assert.assert)((options.data === void 0 ? 0 : 1) + (options.form === void 0 ? 0 : 1) + (options.multipart === void 0 ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
(0, import_assert.assert)(options.maxRedirects === void 0 || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`);
(0, import_assert.assert)(options.maxRetries === void 0 || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`);
const url = options.url !== void 0 ? options.url : options.request.url();
const method = options.method || options.request?.method();
let encodedParams = void 0;
if (typeof options.params === "string")
encodedParams = options.params;
else if (options.params instanceof URLSearchParams)
encodedParams = options.params.toString();
const headersObj = options.headers || options.request?.headers();
const headers = headersObj ? (0, import_headers.headersObjectToArray)(headersObj) : void 0;
let jsonData;
let formData;
let multipartData;
let postDataBuffer;
if (options.data !== void 0) {
if ((0, import_rtti.isString)(options.data)) {
if (isJsonContentType(headers))
jsonData = isJsonParsable(options.data) ? options.data : JSON.stringify(options.data);
else
postDataBuffer = Buffer.from(options.data, "utf8");
} else if (Buffer.isBuffer(options.data)) {
postDataBuffer = options.data;
} else if (typeof options.data === "object" || typeof options.data === "number" || typeof options.data === "boolean") {
jsonData = JSON.stringify(options.data);
} else {
throw new Error(`Unexpected 'data' type`);
}
} else if (options.form) {
if (globalThis.FormData && options.form instanceof FormData) {
formData = [];
for (const [name, value] of options.form.entries()) {
if (typeof value !== "string")
throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
formData.push({ name, value });
}
} else {
formData = objectToArray(options.form);
}
} else if (options.multipart) {
multipartData = [];
if (globalThis.FormData && options.multipart instanceof FormData) {
const form = options.multipart;
for (const [name, value] of form.entries()) {
if ((0, import_rtti.isString)(value)) {
multipartData.push({ name, value });
} else {
const file = {
name: value.name,
mimeType: value.type,
buffer: Buffer.from(await value.arrayBuffer())
};
multipartData.push({ name, file });
}
}
} else {
for (const [name, value] of Object.entries(options.multipart))
multipartData.push(await toFormField(this._platform, name, value));
}
}
if (postDataBuffer === void 0 && jsonData === void 0 && formData === void 0 && multipartData === void 0)
postDataBuffer = options.request?.postDataBuffer() || void 0;
const fixtures = {
__testHookLookup: options.__testHookLookup
};
const result = await this._channel.fetch({
url,
params: typeof options.params === "object" ? objectToArray(options.params) : void 0,
encodedParams,
method,
headers,
postData: postDataBuffer,
jsonData,
formData,
multipartData,
timeout: this._timeoutSettings.timeout(options),
failOnStatusCode: options.failOnStatusCode,
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
maxRedirects: options.maxRedirects,
maxRetries: options.maxRetries,
...fixtures
});
return new APIResponse(this, result.response);
});
}
async storageState(options = {}) {
const state = await this._channel.storageState({ indexedDB: options.indexedDB });
if (options.path) {
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
}
return state;
}
}
async function toFormField(platform, name, value) {
const typeOfValue = typeof value;
if (isFilePayload(value)) {
const payload = value;
if (!Buffer.isBuffer(payload.buffer))
throw new Error(`Unexpected buffer type of 'data.${name}'`);
return { name, file: filePayloadToJson(payload) };
} else if (typeOfValue === "string" || typeOfValue === "number" || typeOfValue === "boolean") {
return { name, value: String(value) };
} else {
return { name, file: await readStreamToJson(platform, value) };
}
}
function isJsonParsable(value) {
if (typeof value !== "string")
return false;
try {
JSON.parse(value);
return true;
} catch (e) {
if (e instanceof SyntaxError)
return false;
else
throw e;
}
}
class APIResponse {
constructor(context, initializer) {
this._request = context;
this._initializer = initializer;
this._headers = new import_network.RawHeaders(this._initializer.headers);
if (context._platform.inspectCustom)
this[context._platform.inspectCustom] = () => this._inspect();
}
ok() {
return this._initializer.status >= 200 && this._initializer.status <= 299;
}
url() {
return this._initializer.url;
}
status() {
return this._initializer.status;
}
statusText() {
return this._initializer.statusText;
}
headers() {
return this._headers.headers();
}
headersArray() {
return this._headers.headersArray();
}
async body() {
return await this._request._wrapApiCall(async () => {
try {
const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
if (result.binary === void 0)
throw new Error("Response has been disposed");
return result.binary;
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
throw new Error("Response has been disposed");
throw e;
}
}, { internal: true });
}
async text() {
const content = await this.body();
return content.toString("utf8");
}
async json() {
const content = await this.text();
return JSON.parse(content);
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
}
_inspect() {
const headers = this.headersArray().map(({ name, value }) => ` ${name}: ${value}`);
return `APIResponse: ${this.status()} ${this.statusText()}
${headers.join("\n")}`;
}
_fetchUid() {
return this._initializer.fetchUid;
}
async _fetchLog() {
const { log } = await this._request._channel.fetchLog({ fetchUid: this._fetchUid() });
return log;
}
}
function filePayloadToJson(payload) {
return {
name: payload.name,
mimeType: payload.mimeType,
buffer: payload.buffer
};
}
async function readStreamToJson(platform, stream) {
const buffer = await new Promise((resolve, reject) => {
const chunks = [];
stream.on("data", (chunk) => chunks.push(chunk));
stream.on("end", () => resolve(Buffer.concat(chunks)));
stream.on("error", (err) => reject(err));
});
const streamPath = Buffer.isBuffer(stream.path) ? stream.path.toString("utf8") : stream.path;
return {
name: platform.path().basename(streamPath),
buffer
};
}
function isJsonContentType(headers) {
if (!headers)
return false;
for (const { name, value } of headers) {
if (name.toLocaleLowerCase() === "content-type")
return value === "application/json";
}
return false;
}
function objectToArray(map) {
if (!map)
return void 0;
const result = [];
for (const [name, value] of Object.entries(map)) {
if (value !== void 0)
result.push({ name, value: String(value) });
}
return result;
}
function isFilePayload(value) {
return typeof value === "object" && value["name"] && value["mimeType"] && value["buffer"];
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
APIRequest,
APIRequestContext,
APIResponse
});
+46
View File
@@ -0,0 +1,46 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fileChooser_exports = {};
__export(fileChooser_exports, {
FileChooser: () => FileChooser
});
module.exports = __toCommonJS(fileChooser_exports);
class FileChooser {
constructor(page, elementHandle, isMultiple) {
this._page = page;
this._elementHandle = elementHandle;
this._isMultiple = isMultiple;
}
element() {
return this._elementHandle;
}
isMultiple() {
return this._isMultiple;
}
page() {
return this._page;
}
async setFiles(files, options) {
return await this._elementHandle.setInputFiles(files, options);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FileChooser
});
+34
View File
@@ -0,0 +1,34 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fileUtils_exports = {};
__export(fileUtils_exports, {
fileUploadSizeLimit: () => fileUploadSizeLimit,
mkdirIfNeeded: () => mkdirIfNeeded
});
module.exports = __toCommonJS(fileUtils_exports);
const fileUploadSizeLimit = 50 * 1024 * 1024;
async function mkdirIfNeeded(platform, filePath) {
await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
fileUploadSizeLimit,
mkdirIfNeeded
});
+404
View File
@@ -0,0 +1,404 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var frame_exports = {};
__export(frame_exports, {
Frame: () => Frame,
verifyLoadState: () => verifyLoadState
});
module.exports = __toCommonJS(frame_exports);
var import_eventEmitter = require("./eventEmitter");
var import_channelOwner = require("./channelOwner");
var import_clientHelper = require("./clientHelper");
var import_elementHandle = require("./elementHandle");
var import_events = require("./events");
var import_jsHandle = require("./jsHandle");
var import_locator = require("./locator");
var network = __toESM(require("./network"));
var import_types = require("./types");
var import_waiter = require("./waiter");
var import_assert = require("../utils/isomorphic/assert");
var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
var import_urlMatch = require("../utils/isomorphic/urlMatch");
var import_timeoutSettings = require("./timeoutSettings");
class Frame extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._parentFrame = null;
this._url = "";
this._name = "";
this._detached = false;
this._childFrames = /* @__PURE__ */ new Set();
this._eventEmitter = new import_eventEmitter.EventEmitter(parent._platform);
this._eventEmitter.setMaxListeners(0);
this._parentFrame = Frame.fromNullable(initializer.parentFrame);
if (this._parentFrame)
this._parentFrame._childFrames.add(this);
this._name = initializer.name;
this._url = initializer.url;
this._loadStates = new Set(initializer.loadStates);
this._channel.on("loadstate", (event) => {
if (event.add) {
this._loadStates.add(event.add);
this._eventEmitter.emit("loadstate", event.add);
}
if (event.remove)
this._loadStates.delete(event.remove);
if (!this._parentFrame && event.add === "load" && this._page)
this._page.emit(import_events.Events.Page.Load, this._page);
if (!this._parentFrame && event.add === "domcontentloaded" && this._page)
this._page.emit(import_events.Events.Page.DOMContentLoaded, this._page);
});
this._channel.on("navigated", (event) => {
this._url = event.url;
this._name = event.name;
this._eventEmitter.emit("navigated", event);
if (!event.error && this._page)
this._page.emit(import_events.Events.Page.FrameNavigated, this);
});
}
static from(frame) {
return frame._object;
}
static fromNullable(frame) {
return frame ? Frame.from(frame) : null;
}
page() {
return this._page;
}
_timeout(options) {
const timeoutSettings = this._page?._timeoutSettings || new import_timeoutSettings.TimeoutSettings(this._platform);
return timeoutSettings.timeout(options || {});
}
_navigationTimeout(options) {
const timeoutSettings = this._page?._timeoutSettings || new import_timeoutSettings.TimeoutSettings(this._platform);
return timeoutSettings.navigationTimeout(options || {});
}
async goto(url, options = {}) {
const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil, timeout: this._navigationTimeout(options) })).response);
}
_setupNavigationWaiter(options) {
const waiter = new import_waiter.Waiter(this._page, "");
if (this._page.isClosed())
waiter.rejectImmediately(this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, import_events.Events.Page.Crash, new Error("Navigation failed because page crashed!"));
waiter.rejectOnEvent(this._page, import_events.Events.Page.FrameDetached, new Error("Navigating frame was detached!"), (frame) => frame === this);
const timeout = this._page._timeoutSettings.navigationTimeout(options);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`);
return waiter;
}
async waitForNavigation(options = {}) {
return await this._page._wrapApiCall(async () => {
const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
const waiter = this._setupNavigationWaiter(options);
const toUrl = typeof options.url === "string" ? ` to "${options.url}"` : "";
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, "navigated", (event) => {
if (event.error)
return true;
waiter.log(` navigated to "${event.url}"`);
return (0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, event.url, options.url);
});
if (navigatedEvent.error) {
const e = new Error(navigatedEvent.error);
e.stack = "";
await waiter.waitForPromise(Promise.reject(e));
}
if (!this._loadStates.has(waitUntil)) {
await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`);
return s === waitUntil;
});
}
const request = navigatedEvent.newDocument ? network.Request.fromNullable(navigatedEvent.newDocument.request) : null;
const response = request ? await waiter.waitForPromise(request._finalRequest()._internalResponse()) : null;
waiter.dispose();
return response;
}, { title: "Wait for navigation" });
}
async waitForLoadState(state = "load", options = {}) {
state = verifyLoadState("state", state);
return await this._page._wrapApiCall(async () => {
const waiter = this._setupNavigationWaiter(options);
if (this._loadStates.has(state)) {
waiter.log(` not waiting, "${state}" event already fired`);
} else {
await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`);
return s === state;
});
}
waiter.dispose();
}, { title: `Wait for load state "${state}"` });
}
async waitForURL(url, options = {}) {
if ((0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, this.url(), url))
return await this.waitForLoadState(options.waitUntil, options);
await this.waitForNavigation({ url, ...options });
}
async frameElement() {
return import_elementHandle.ElementHandle.from((await this._channel.frameElement()).element);
}
async evaluateHandle(pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return import_jsHandle.JSHandle.from(result.handle);
}
async evaluate(pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async _evaluateExposeUtilityScript(pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async $(selector, options) {
const result = await this._channel.querySelector({ selector, ...options });
return import_elementHandle.ElementHandle.fromNullable(result.element);
}
async waitForSelector(selector, options = {}) {
if (options.visibility)
throw new Error("options.visibility is not supported, did you mean options.state?");
if (options.waitFor && options.waitFor !== "visible")
throw new Error("options.waitFor is not supported, did you mean options.state?");
const result = await this._channel.waitForSelector({ selector, ...options, timeout: this._timeout(options) });
return import_elementHandle.ElementHandle.fromNullable(result.element);
}
async dispatchEvent(selector, type, eventInit, options = {}) {
await this._channel.dispatchEvent({ selector, type, eventInit: (0, import_jsHandle.serializeArgument)(eventInit), ...options, timeout: this._timeout(options) });
}
async $eval(selector, pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
const result = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async $$eval(selector, pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
const result = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
return (0, import_jsHandle.parseResult)(result.value);
}
async $$(selector) {
const result = await this._channel.querySelectorAll({ selector });
return result.elements.map((e) => import_elementHandle.ElementHandle.from(e));
}
async _queryCount(selector, options) {
return (await this._channel.queryCount({ selector, ...options })).value;
}
async content() {
return (await this._channel.content()).value;
}
async setContent(html, options = {}) {
const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
await this._channel.setContent({ html, ...options, waitUntil, timeout: this._navigationTimeout(options) });
}
name() {
return this._name || "";
}
url() {
return this._url;
}
parentFrame() {
return this._parentFrame;
}
childFrames() {
return Array.from(this._childFrames);
}
isDetached() {
return this._detached;
}
async addScriptTag(options = {}) {
const copy = { ...options };
if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content = (0, import_clientHelper.addSourceUrlToScript)(copy.content, copy.path);
}
return import_elementHandle.ElementHandle.from((await this._channel.addScriptTag({ ...copy })).element);
}
async addStyleTag(options = {}) {
const copy = { ...options };
if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content += "/*# sourceURL=" + copy.path.replace(/\n/g, "") + "*/";
}
return import_elementHandle.ElementHandle.from((await this._channel.addStyleTag({ ...copy })).element);
}
async click(selector, options = {}) {
return await this._channel.click({ selector, ...options, timeout: this._timeout(options) });
}
async dblclick(selector, options = {}) {
return await this._channel.dblclick({ selector, ...options, timeout: this._timeout(options) });
}
async dragAndDrop(source, target, options = {}) {
return await this._channel.dragAndDrop({ source, target, ...options, timeout: this._timeout(options) });
}
async tap(selector, options = {}) {
return await this._channel.tap({ selector, ...options, timeout: this._timeout(options) });
}
async fill(selector, value, options = {}) {
return await this._channel.fill({ selector, value, ...options, timeout: this._timeout(options) });
}
async _highlight(selector) {
return await this._channel.highlight({ selector });
}
locator(selector, options) {
return new import_locator.Locator(this, selector, options);
}
getByTestId(testId) {
return this.locator((0, import_locatorUtils.getByTestIdSelector)((0, import_locator.testIdAttributeName)(), testId));
}
getByAltText(text, options) {
return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
}
getByLabel(text, options) {
return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
}
getByPlaceholder(text, options) {
return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
}
getByText(text, options) {
return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
}
getByTitle(text, options) {
return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
}
getByRole(role, options = {}) {
return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
}
frameLocator(selector) {
return new import_locator.FrameLocator(this, selector);
}
async focus(selector, options = {}) {
await this._channel.focus({ selector, ...options, timeout: this._timeout(options) });
}
async textContent(selector, options = {}) {
const value = (await this._channel.textContent({ selector, ...options, timeout: this._timeout(options) })).value;
return value === void 0 ? null : value;
}
async innerText(selector, options = {}) {
return (await this._channel.innerText({ selector, ...options, timeout: this._timeout(options) })).value;
}
async innerHTML(selector, options = {}) {
return (await this._channel.innerHTML({ selector, ...options, timeout: this._timeout(options) })).value;
}
async getAttribute(selector, name, options = {}) {
const value = (await this._channel.getAttribute({ selector, name, ...options, timeout: this._timeout(options) })).value;
return value === void 0 ? null : value;
}
async inputValue(selector, options = {}) {
return (await this._channel.inputValue({ selector, ...options, timeout: this._timeout(options) })).value;
}
async isChecked(selector, options = {}) {
return (await this._channel.isChecked({ selector, ...options, timeout: this._timeout(options) })).value;
}
async isDisabled(selector, options = {}) {
return (await this._channel.isDisabled({ selector, ...options, timeout: this._timeout(options) })).value;
}
async isEditable(selector, options = {}) {
return (await this._channel.isEditable({ selector, ...options, timeout: this._timeout(options) })).value;
}
async isEnabled(selector, options = {}) {
return (await this._channel.isEnabled({ selector, ...options, timeout: this._timeout(options) })).value;
}
async isHidden(selector, options = {}) {
return (await this._channel.isHidden({ selector, ...options })).value;
}
async isVisible(selector, options = {}) {
return (await this._channel.isVisible({ selector, ...options })).value;
}
async hover(selector, options = {}) {
await this._channel.hover({ selector, ...options, timeout: this._timeout(options) });
}
async selectOption(selector, values, options = {}) {
return (await this._channel.selectOption({ selector, ...(0, import_elementHandle.convertSelectOptionValues)(values), ...options, timeout: this._timeout(options) })).values;
}
async setInputFiles(selector, files, options = {}) {
const converted = await (0, import_elementHandle.convertInputFiles)(this._platform, files, this.page().context());
await this._channel.setInputFiles({ selector, ...converted, ...options, timeout: this._timeout(options) });
}
async type(selector, text, options = {}) {
await this._channel.type({ selector, text, ...options, timeout: this._timeout(options) });
}
async press(selector, key, options = {}) {
await this._channel.press({ selector, key, ...options, timeout: this._timeout(options) });
}
async check(selector, options = {}) {
await this._channel.check({ selector, ...options, timeout: this._timeout(options) });
}
async uncheck(selector, options = {}) {
await this._channel.uncheck({ selector, ...options, timeout: this._timeout(options) });
}
async setChecked(selector, checked, options) {
if (checked)
await this.check(selector, options);
else
await this.uncheck(selector, options);
}
async waitForTimeout(timeout) {
await this._channel.waitForTimeout({ waitTimeout: timeout });
}
async waitForFunction(pageFunction, arg, options = {}) {
if (typeof options.polling === "string")
(0, import_assert.assert)(options.polling === "raf", "Unknown polling option: " + options.polling);
const result = await this._channel.waitForFunction({
...options,
pollingInterval: options.polling === "raf" ? void 0 : options.polling,
expression: String(pageFunction),
isFunction: typeof pageFunction === "function",
arg: (0, import_jsHandle.serializeArgument)(arg),
timeout: this._timeout(options)
});
return import_jsHandle.JSHandle.from(result.handle);
}
async title() {
return (await this._channel.title()).value;
}
async _expect(expression, options) {
const params = { expression, ...options, isNot: !!options.isNot };
params.expectedValue = (0, import_jsHandle.serializeArgument)(options.expectedValue);
const result = await this._channel.expect(params);
if (result.received !== void 0)
result.received = (0, import_jsHandle.parseResult)(result.received);
return result;
}
}
function verifyLoadState(name, waitUntil) {
if (waitUntil === "networkidle0")
waitUntil = "networkidle";
if (!import_types.kLifecycleEvents.has(waitUntil))
throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
return waitUntil;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Frame,
verifyLoadState
});
+99
View File
@@ -0,0 +1,99 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var harRouter_exports = {};
__export(harRouter_exports, {
HarRouter: () => HarRouter
});
module.exports = __toCommonJS(harRouter_exports);
class HarRouter {
static async create(localUtils, file, notFoundAction, options) {
const { harId, error } = await localUtils.harOpen({ file });
if (error)
throw new Error(error);
return new HarRouter(localUtils, harId, notFoundAction, options);
}
constructor(localUtils, harId, notFoundAction, options) {
this._localUtils = localUtils;
this._harId = harId;
this._options = options;
this._notFoundAction = notFoundAction;
}
async _handle(route) {
const request = route.request();
const response = await this._localUtils.harLookup({
harId: this._harId,
url: request.url(),
method: request.method(),
headers: await request.headersArray(),
postData: request.postDataBuffer() || void 0,
isNavigationRequest: request.isNavigationRequest()
});
if (response.action === "redirect") {
route._platform.log("api", `HAR: ${route.request().url()} redirected to ${response.redirectURL}`);
await route._redirectNavigationRequest(response.redirectURL);
return;
}
if (response.action === "fulfill") {
if (response.status === -1)
return;
const transformedHeaders = response.headers.reduce((headersMap, { name, value }) => {
if (name.toLowerCase() !== "set-cookie") {
headersMap[name] = value;
} else {
if (!headersMap["set-cookie"])
headersMap["set-cookie"] = value;
else
headersMap["set-cookie"] += `
${value}`;
}
return headersMap;
}, {});
await route.fulfill({
status: response.status,
headers: transformedHeaders,
body: response.body
});
return;
}
if (response.action === "error")
route._platform.log("api", "HAR: " + response.message);
if (this._notFoundAction === "abort") {
await route.abort();
return;
}
await route.fallback();
}
async addContextRoute(context) {
await context.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
}
async addPageRoute(page) {
await page.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
dispose() {
this._localUtils.harClose({ harId: this._harId }).catch(() => {
});
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
HarRouter
});
+84
View File
@@ -0,0 +1,84 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var input_exports = {};
__export(input_exports, {
Keyboard: () => Keyboard,
Mouse: () => Mouse,
Touchscreen: () => Touchscreen
});
module.exports = __toCommonJS(input_exports);
class Keyboard {
constructor(page) {
this._page = page;
}
async down(key) {
await this._page._channel.keyboardDown({ key });
}
async up(key) {
await this._page._channel.keyboardUp({ key });
}
async insertText(text) {
await this._page._channel.keyboardInsertText({ text });
}
async type(text, options = {}) {
await this._page._channel.keyboardType({ text, ...options });
}
async press(key, options = {}) {
await this._page._channel.keyboardPress({ key, ...options });
}
}
class Mouse {
constructor(page) {
this._page = page;
}
async move(x, y, options = {}) {
await this._page._channel.mouseMove({ x, y, ...options });
}
async down(options = {}) {
await this._page._channel.mouseDown({ ...options });
}
async up(options = {}) {
await this._page._channel.mouseUp(options);
}
async click(x, y, options = {}) {
await this._page._channel.mouseClick({ x, y, ...options });
}
async dblclick(x, y, options = {}) {
await this._page._wrapApiCall(async () => {
await this.click(x, y, { ...options, clickCount: 2 });
}, { title: "Double click" });
}
async wheel(deltaX, deltaY) {
await this._page._channel.mouseWheel({ deltaX, deltaY });
}
}
class Touchscreen {
constructor(page) {
this._page = page;
}
async tap(x, y) {
await this._page._channel.touchscreenTap({ x, y });
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Keyboard,
Mouse,
Touchscreen
});
+105
View File
@@ -0,0 +1,105 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var jsHandle_exports = {};
__export(jsHandle_exports, {
JSHandle: () => JSHandle,
assertMaxArguments: () => assertMaxArguments,
parseResult: () => parseResult,
serializeArgument: () => serializeArgument
});
module.exports = __toCommonJS(jsHandle_exports);
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_serializers = require("../protocol/serializers");
class JSHandle extends import_channelOwner.ChannelOwner {
static from(handle) {
return handle._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._preview = this._initializer.preview;
this._channel.on("previewUpdated", ({ preview }) => this._preview = preview);
}
async evaluate(pageFunction, arg) {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return parseResult(result.value);
}
async evaluateHandle(pageFunction, arg) {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
return JSHandle.from(result.handle);
}
async getProperty(propertyName) {
const result = await this._channel.getProperty({ name: propertyName });
return JSHandle.from(result.handle);
}
async getProperties() {
const map = /* @__PURE__ */ new Map();
for (const { name, value } of (await this._channel.getPropertyList()).properties)
map.set(name, JSHandle.from(value));
return map;
}
async jsonValue() {
return parseResult((await this._channel.jsonValue()).value);
}
asElement() {
return null;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
async dispose() {
try {
await this._channel.dispose();
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e))
return;
throw e;
}
}
toString() {
return this._preview;
}
}
function serializeArgument(arg) {
const handles = [];
const pushHandle = (channel) => {
handles.push(channel);
return handles.length - 1;
};
const value = (0, import_serializers.serializeValue)(arg, (value2) => {
if (value2 instanceof JSHandle)
return { h: pushHandle(value2._channel) };
return { fallThrough: value2 };
});
return { value, handles };
}
function parseResult(value) {
return (0, import_serializers.parseSerializedValue)(value, void 0);
}
function assertMaxArguments(count, max) {
if (count > max)
throw new Error("Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.");
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
JSHandle,
assertMaxArguments,
parseResult,
serializeArgument
});
+39
View File
@@ -0,0 +1,39 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var jsonPipe_exports = {};
__export(jsonPipe_exports, {
JsonPipe: () => JsonPipe
});
module.exports = __toCommonJS(jsonPipe_exports);
var import_channelOwner = require("./channelOwner");
class JsonPipe extends import_channelOwner.ChannelOwner {
static from(jsonPipe) {
return jsonPipe._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
}
channel() {
return this._channel;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
JsonPipe
});
+60
View File
@@ -0,0 +1,60 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var localUtils_exports = {};
__export(localUtils_exports, {
LocalUtils: () => LocalUtils
});
module.exports = __toCommonJS(localUtils_exports);
var import_channelOwner = require("./channelOwner");
class LocalUtils extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this.devices = {};
for (const { name, descriptor } of initializer.deviceDescriptors)
this.devices[name] = descriptor;
}
async zip(params) {
return await this._channel.zip(params);
}
async harOpen(params) {
return await this._channel.harOpen(params);
}
async harLookup(params) {
return await this._channel.harLookup(params);
}
async harClose(params) {
return await this._channel.harClose(params);
}
async harUnzip(params) {
return await this._channel.harUnzip(params);
}
async tracingStarted(params) {
return await this._channel.tracingStarted(params);
}
async traceDiscarded(params) {
return await this._channel.traceDiscarded(params);
}
async addStackToTracingNoReply(params) {
return await this._channel.addStackToTracingNoReply(params);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
LocalUtils
});
+367
View File
@@ -0,0 +1,367 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var locator_exports = {};
__export(locator_exports, {
FrameLocator: () => FrameLocator,
Locator: () => Locator,
setTestIdAttribute: () => setTestIdAttribute,
testIdAttributeName: () => testIdAttributeName
});
module.exports = __toCommonJS(locator_exports);
var import_elementHandle = require("./elementHandle");
var import_locatorGenerators = require("../utils/isomorphic/locatorGenerators");
var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
var import_stringUtils = require("../utils/isomorphic/stringUtils");
var import_rtti = require("../utils/isomorphic/rtti");
var import_time = require("../utils/isomorphic/time");
class Locator {
constructor(frame, selector, options) {
this._frame = frame;
this._selector = selector;
if (options?.hasText)
this._selector += ` >> internal:has-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasText, false)}`;
if (options?.hasNotText)
this._selector += ` >> internal:has-not-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasNotText, false)}`;
if (options?.has) {
const locator = options.has;
if (locator._frame !== frame)
throw new Error(`Inner "has" locator must belong to the same frame.`);
this._selector += ` >> internal:has=` + JSON.stringify(locator._selector);
}
if (options?.hasNot) {
const locator = options.hasNot;
if (locator._frame !== frame)
throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
this._selector += ` >> internal:has-not=` + JSON.stringify(locator._selector);
}
if (options?.visible !== void 0)
this._selector += ` >> visible=${options.visible ? "true" : "false"}`;
if (this._frame._platform.inspectCustom)
this[this._frame._platform.inspectCustom] = () => this._inspect();
}
async _withElement(task, options) {
const timeout = this._frame._timeout({ timeout: options.timeout });
const deadline = timeout ? (0, import_time.monotonicTime)() + timeout : 0;
return await this._frame._wrapApiCall(async () => {
const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: "attached", timeout });
const handle = import_elementHandle.ElementHandle.fromNullable(result.element);
if (!handle)
throw new Error(`Could not resolve ${this._selector} to DOM Element`);
try {
return await task(handle, deadline ? deadline - (0, import_time.monotonicTime)() : 0);
} finally {
await handle.dispose();
}
}, { title: options.title, internal: options.internal });
}
_equals(locator) {
return this._frame === locator._frame && this._selector === locator._selector;
}
page() {
return this._frame.page();
}
async boundingBox(options) {
return await this._withElement((h) => h.boundingBox(), { title: "Bounding box", timeout: options?.timeout });
}
async check(options = {}) {
return await this._frame.check(this._selector, { strict: true, ...options });
}
async click(options = {}) {
return await this._frame.click(this._selector, { strict: true, ...options });
}
async dblclick(options = {}) {
await this._frame.dblclick(this._selector, { strict: true, ...options });
}
async dispatchEvent(type, eventInit = {}, options) {
return await this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options });
}
async dragTo(target, options = {}) {
return await this._frame.dragAndDrop(this._selector, target._selector, {
strict: true,
...options
});
}
async evaluate(pageFunction, arg, options) {
return await this._withElement((h) => h.evaluate(pageFunction, arg), { title: "Evaluate", timeout: options?.timeout });
}
async evaluateAll(pageFunction, arg) {
return await this._frame.$$eval(this._selector, pageFunction, arg);
}
async evaluateHandle(pageFunction, arg, options) {
return await this._withElement((h) => h.evaluateHandle(pageFunction, arg), { title: "Evaluate", timeout: options?.timeout });
}
async fill(value, options = {}) {
return await this._frame.fill(this._selector, value, { strict: true, ...options });
}
async clear(options = {}) {
await this._frame._wrapApiCall(() => this.fill("", options), { title: "Clear" });
}
async _highlight() {
return await this._frame._highlight(this._selector);
}
async highlight() {
return await this._frame._highlight(this._selector);
}
locator(selectorOrLocator, options) {
if ((0, import_rtti.isString)(selectorOrLocator))
return new Locator(this._frame, this._selector + " >> " + selectorOrLocator, options);
if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + " >> internal:chain=" + JSON.stringify(selectorOrLocator._selector), options);
}
getByTestId(testId) {
return this.locator((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
}
getByAltText(text, options) {
return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
}
getByLabel(text, options) {
return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
}
getByPlaceholder(text, options) {
return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
}
getByText(text, options) {
return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
}
getByTitle(text, options) {
return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
}
getByRole(role, options = {}) {
return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
}
frameLocator(selector) {
return new FrameLocator(this._frame, this._selector + " >> " + selector);
}
filter(options) {
return new Locator(this._frame, this._selector, options);
}
async elementHandle(options) {
return await this._frame.waitForSelector(this._selector, { strict: true, state: "attached", ...options });
}
async elementHandles() {
return await this._frame.$$(this._selector);
}
contentFrame() {
return new FrameLocator(this._frame, this._selector);
}
describe(description) {
return new Locator(this._frame, this._selector + " >> internal:describe=" + JSON.stringify(description));
}
description() {
return (0, import_locatorGenerators.locatorCustomDescription)(this._selector) || null;
}
first() {
return new Locator(this._frame, this._selector + " >> nth=0");
}
last() {
return new Locator(this._frame, this._selector + ` >> nth=-1`);
}
nth(index) {
return new Locator(this._frame, this._selector + ` >> nth=${index}`);
}
and(locator) {
if (locator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator._selector));
}
or(locator) {
if (locator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator._selector));
}
async focus(options) {
return await this._frame.focus(this._selector, { strict: true, ...options });
}
async blur(options) {
await this._frame._channel.blur({ selector: this._selector, strict: true, ...options, timeout: this._frame._timeout(options) });
}
// options are only here for testing
async count(_options) {
return await this._frame._queryCount(this._selector, _options);
}
async normalize() {
const { resolvedSelector } = await this._frame._channel.resolveSelector({ selector: this._selector });
return new Locator(this._frame, resolvedSelector);
}
async getAttribute(name, options) {
return await this._frame.getAttribute(this._selector, name, { strict: true, ...options });
}
async hover(options = {}) {
return await this._frame.hover(this._selector, { strict: true, ...options });
}
async innerHTML(options) {
return await this._frame.innerHTML(this._selector, { strict: true, ...options });
}
async innerText(options) {
return await this._frame.innerText(this._selector, { strict: true, ...options });
}
async inputValue(options) {
return await this._frame.inputValue(this._selector, { strict: true, ...options });
}
async isChecked(options) {
return await this._frame.isChecked(this._selector, { strict: true, ...options });
}
async isDisabled(options) {
return await this._frame.isDisabled(this._selector, { strict: true, ...options });
}
async isEditable(options) {
return await this._frame.isEditable(this._selector, { strict: true, ...options });
}
async isEnabled(options) {
return await this._frame.isEnabled(this._selector, { strict: true, ...options });
}
async isHidden(options) {
return await this._frame.isHidden(this._selector, { strict: true, ...options });
}
async isVisible(options) {
return await this._frame.isVisible(this._selector, { strict: true, ...options });
}
async press(key, options = {}) {
return await this._frame.press(this._selector, key, { strict: true, ...options });
}
async screenshot(options = {}) {
const mask = options.mask;
return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), { title: "Screenshot", timeout: options.timeout });
}
async ariaSnapshot(options = {}) {
const result = await this._frame._channel.ariaSnapshot({ timeout: this._frame._timeout(options), mode: options.mode, selector: this._selector, depth: options.depth });
return result.snapshot;
}
async scrollIntoViewIfNeeded(options = {}) {
return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), { title: "Scroll into view", timeout: options.timeout });
}
async selectOption(values, options = {}) {
return await this._frame.selectOption(this._selector, values, { strict: true, ...options });
}
async selectText(options = {}) {
return await this._withElement((h, timeout) => h.selectText({ ...options, timeout }), { title: "Select text", timeout: options.timeout });
}
async setChecked(checked, options) {
if (checked)
await this.check(options);
else
await this.uncheck(options);
}
async setInputFiles(files, options = {}) {
return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options });
}
async tap(options = {}) {
return await this._frame.tap(this._selector, { strict: true, ...options });
}
async textContent(options) {
return await this._frame.textContent(this._selector, { strict: true, ...options });
}
async type(text, options = {}) {
return await this._frame.type(this._selector, text, { strict: true, ...options });
}
async pressSequentially(text, options = {}) {
return await this.type(text, options);
}
async uncheck(options = {}) {
return await this._frame.uncheck(this._selector, { strict: true, ...options });
}
async all() {
return new Array(await this.count()).fill(0).map((e, i) => this.nth(i));
}
async allInnerTexts() {
return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.innerText));
}
async allTextContents() {
return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.textContent || ""));
}
async waitFor(options) {
await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options, timeout: this._frame._timeout(options) });
}
async _expect(expression, options) {
return this._frame._expect(expression, {
...options,
selector: this._selector
});
}
_inspect() {
return this.toString();
}
toString() {
return (0, import_locatorGenerators.asLocatorDescription)("javascript", this._selector);
}
}
class FrameLocator {
constructor(frame, selector) {
this._frame = frame;
this._frameSelector = selector;
}
locator(selectorOrLocator, options) {
if ((0, import_rtti.isString)(selectorOrLocator))
return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator, options);
if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator._selector, options);
}
getByTestId(testId) {
return this.locator((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
}
getByAltText(text, options) {
return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
}
getByLabel(text, options) {
return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
}
getByPlaceholder(text, options) {
return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
}
getByText(text, options) {
return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
}
getByTitle(text, options) {
return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
}
getByRole(role, options = {}) {
return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
}
owner() {
return new Locator(this._frame, this._frameSelector);
}
frameLocator(selector) {
return new FrameLocator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selector);
}
first() {
return new FrameLocator(this._frame, this._frameSelector + " >> nth=0");
}
last() {
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`);
}
nth(index) {
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`);
}
}
let _testIdAttributeName = "data-testid";
function testIdAttributeName() {
return _testIdAttributeName;
}
function setTestIdAttribute(attributeName) {
_testIdAttributeName = attributeName;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FrameLocator,
Locator,
setTestIdAttribute,
testIdAttributeName
});
+750
View File
@@ -0,0 +1,750 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var network_exports = {};
__export(network_exports, {
RawHeaders: () => RawHeaders,
Request: () => Request,
Response: () => Response,
Route: () => Route,
RouteHandler: () => RouteHandler,
WebSocket: () => WebSocket,
WebSocketRoute: () => WebSocketRoute,
WebSocketRouteHandler: () => WebSocketRouteHandler,
validateHeaders: () => validateHeaders
});
module.exports = __toCommonJS(network_exports);
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_events = require("./events");
var import_fetch = require("./fetch");
var import_frame = require("./frame");
var import_waiter = require("./waiter");
var import_worker = require("./worker");
var import_assert = require("../utils/isomorphic/assert");
var import_headers = require("../utils/isomorphic/headers");
var import_urlMatch = require("../utils/isomorphic/urlMatch");
var import_manualPromise = require("../utils/isomorphic/manualPromise");
var import_multimap = require("../utils/isomorphic/multimap");
var import_rtti = require("../utils/isomorphic/rtti");
var import_stackTrace = require("../utils/isomorphic/stackTrace");
var import_mimeType = require("../utils/isomorphic/mimeType");
class Request extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._redirectedFrom = null;
this._redirectedTo = null;
this._failureText = null;
this._response = null;
this._fallbackOverrides = {};
this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this;
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._timing = {
startTime: 0,
domainLookupStart: -1,
domainLookupEnd: -1,
connectStart: -1,
secureConnectionStart: -1,
connectEnd: -1,
requestStart: -1,
responseStart: -1,
responseEnd: -1
};
}
static from(request) {
return request._object;
}
static fromNullable(request) {
return request ? Request.from(request) : null;
}
url() {
return this._fallbackOverrides.url || this._initializer.url;
}
resourceType() {
return this._initializer.resourceType;
}
method() {
return this._fallbackOverrides.method || this._initializer.method;
}
postData() {
return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString("utf-8") || null;
}
postDataBuffer() {
return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null;
}
postDataJSON() {
const postData = this.postData();
if (!postData)
return null;
const contentType = this.headers()["content-type"];
if (contentType?.includes("application/x-www-form-urlencoded")) {
const entries = {};
const parsed = new URLSearchParams(postData);
for (const [k, v] of parsed.entries())
entries[k] = v;
return entries;
}
try {
return JSON.parse(postData);
} catch (e) {
throw new Error("POST data is not a valid JSON object: " + postData);
}
}
/**
* @deprecated
*/
headers() {
if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers();
return this._provisionalHeaders.headers();
}
async _actualHeaders() {
if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers);
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async () => {
return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
}, { internal: true });
}
return await this._actualHeadersPromise;
}
async allHeaders() {
return (await this._actualHeaders()).headers();
}
async headersArray() {
return (await this._actualHeaders()).headersArray();
}
async headerValue(name) {
return (await this._actualHeaders()).get(name);
}
async response() {
return Response.fromNullable((await this._channel.response()).response);
}
async _internalResponse() {
return Response.fromNullable((await this._channel.response()).response);
}
existingResponse() {
return this._response;
}
frame() {
if (!this._initializer.frame) {
(0, import_assert.assert)(this.serviceWorker());
throw new Error("Service Worker requests do not have an associated frame.");
}
const frame = import_frame.Frame.from(this._initializer.frame);
if (!frame._page) {
throw new Error([
"Frame for this navigation request is not available, because the request",
"was issued before the frame is created. You can check whether the request",
"is a navigation request by calling isNavigationRequest() method."
].join("\n"));
}
return frame;
}
_safePage() {
return import_frame.Frame.fromNullable(this._initializer.frame)?._page || null;
}
serviceWorker() {
return this._initializer.serviceWorker ? import_worker.Worker.from(this._initializer.serviceWorker) : null;
}
isNavigationRequest() {
return this._initializer.isNavigationRequest;
}
redirectedFrom() {
return this._redirectedFrom;
}
redirectedTo() {
return this._redirectedTo;
}
failure() {
if (this._failureText === null)
return null;
return {
errorText: this._failureText
};
}
timing() {
return this._timing;
}
async sizes() {
const response = await this.response();
if (!response)
throw new Error("Unable to fetch sizes for failed request");
return (await response._channel.sizes()).sizes;
}
_setResponseEndTiming(responseEndTiming) {
this._timing.responseEnd = responseEndTiming;
if (this._timing.responseStart === -1)
this._timing.responseStart = responseEndTiming;
}
_finalRequest() {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
}
_applyFallbackOverrides(overrides) {
if (overrides.url)
this._fallbackOverrides.url = overrides.url;
if (overrides.method)
this._fallbackOverrides.method = overrides.method;
if (overrides.headers)
this._fallbackOverrides.headers = overrides.headers;
if ((0, import_rtti.isString)(overrides.postData))
this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, "utf-8");
else if (overrides.postData instanceof Buffer)
this._fallbackOverrides.postDataBuffer = overrides.postData;
else if (overrides.postData)
this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), "utf-8");
}
_fallbackOverridesForContinue() {
return this._fallbackOverrides;
}
_targetClosedScope() {
return this.serviceWorker()?._closedScope || this._safePage()?._closedOrCrashedScope || new import_manualPromise.LongStandingScope();
}
}
class Route extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._handlingPromise = null;
this._didThrow = false;
}
static from(route) {
return route._object;
}
request() {
return Request.from(this._initializer.request);
}
async _raceWithTargetClose(promise) {
return await this.request()._targetClosedScope().safeRace(promise);
}
async _startHandling() {
this._handlingPromise = new import_manualPromise.ManualPromise();
return await this._handlingPromise;
}
async fallback(options = {}) {
this._checkNotHandled();
this.request()._applyFallbackOverrides(options);
this._reportHandled(false);
}
async abort(errorCode) {
await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.abort({ errorCode }));
});
}
async _redirectNavigationRequest(url) {
await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ url }));
});
}
async fetch(options = {}) {
return await this._wrapApiCall(async () => {
return await this._context.request._innerFetch({ request: this.request(), data: options.postData, ...options });
});
}
async fulfill(options = {}) {
await this._handleRoute(async () => {
await this._innerFulfill(options);
});
}
async _handleRoute(callback) {
this._checkNotHandled();
try {
await callback();
this._reportHandled(true);
} catch (e) {
this._didThrow = true;
throw e;
}
}
async _innerFulfill(options = {}) {
let fetchResponseUid;
let { status: statusOption, headers: headersOption, body } = options;
if (options.json !== void 0) {
(0, import_assert.assert)(options.body === void 0, "Can specify either body or json parameters");
body = JSON.stringify(options.json);
}
if (options.response instanceof import_fetch.APIResponse) {
statusOption ??= options.response.status();
headersOption ??= options.response.headers();
if (body === void 0 && options.path === void 0) {
if (options.response._request._connection === this._connection)
fetchResponseUid = options.response._fetchUid();
else
body = await options.response.body();
}
}
let isBase64 = false;
let length = 0;
if (options.path) {
const buffer = await this._platform.fs().promises.readFile(options.path);
body = buffer.toString("base64");
isBase64 = true;
length = buffer.length;
} else if ((0, import_rtti.isString)(body)) {
isBase64 = false;
length = Buffer.byteLength(body);
} else if (body) {
length = body.length;
body = body.toString("base64");
isBase64 = true;
}
const headers = {};
for (const header of Object.keys(headersOption || {}))
headers[header.toLowerCase()] = String(headersOption[header]);
if (options.contentType)
headers["content-type"] = String(options.contentType);
else if (options.json)
headers["content-type"] = "application/json";
else if (options.path)
headers["content-type"] = (0, import_mimeType.getMimeTypeForPath)(options.path) || "application/octet-stream";
if (length && !("content-length" in headers))
headers["content-length"] = String(length);
await this._raceWithTargetClose(this._channel.fulfill({
status: statusOption || 200,
headers: (0, import_headers.headersObjectToArray)(headers),
body,
isBase64,
fetchResponseUid
}));
}
async continue(options = {}) {
await this._handleRoute(async () => {
this.request()._applyFallbackOverrides(options);
await this._innerContinue(
false
/* isFallback */
);
});
}
_checkNotHandled() {
if (!this._handlingPromise)
throw new Error("Route is already handled!");
}
_reportHandled(done) {
const chain = this._handlingPromise;
this._handlingPromise = null;
chain.resolve(done);
}
async _innerContinue(isFallback) {
const options = this.request()._fallbackOverridesForContinue();
return await this._raceWithTargetClose(this._channel.continue({
url: options.url,
method: options.method,
headers: options.headers ? (0, import_headers.headersObjectToArray)(options.headers) : void 0,
postData: options.postDataBuffer,
isFallback
}));
}
}
class WebSocketRoute extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._connected = false;
this._server = {
onMessage: (handler) => {
this._onServerMessage = handler;
},
onClose: (handler) => {
this._onServerClose = handler;
},
connectToServer: () => {
throw new Error(`connectToServer must be called on the page-side WebSocketRoute`);
},
url: () => {
return this._initializer.url;
},
close: async (options = {}) => {
await this._channel.closeServer({ ...options, wasClean: true }).catch(() => {
});
},
send: (message) => {
if ((0, import_rtti.isString)(message))
this._channel.sendToServer({ message, isBase64: false }).catch(() => {
});
else
this._channel.sendToServer({ message: message.toString("base64"), isBase64: true }).catch(() => {
});
},
async [Symbol.asyncDispose]() {
await this.close();
}
};
this._channel.on("messageFromPage", ({ message, isBase64 }) => {
if (this._onPageMessage)
this._onPageMessage(isBase64 ? Buffer.from(message, "base64") : message);
else if (this._connected)
this._channel.sendToServer({ message, isBase64 }).catch(() => {
});
});
this._channel.on("messageFromServer", ({ message, isBase64 }) => {
if (this._onServerMessage)
this._onServerMessage(isBase64 ? Buffer.from(message, "base64") : message);
else
this._channel.sendToPage({ message, isBase64 }).catch(() => {
});
});
this._channel.on("closePage", ({ code, reason, wasClean }) => {
if (this._onPageClose)
this._onPageClose(code, reason);
else
this._channel.closeServer({ code, reason, wasClean }).catch(() => {
});
});
this._channel.on("closeServer", ({ code, reason, wasClean }) => {
if (this._onServerClose)
this._onServerClose(code, reason);
else
this._channel.closePage({ code, reason, wasClean }).catch(() => {
});
});
}
static from(route) {
return route._object;
}
url() {
return this._initializer.url;
}
async close(options = {}) {
await this._channel.closePage({ ...options, wasClean: true }).catch(() => {
});
}
connectToServer() {
if (this._connected)
throw new Error("Already connected to the server");
this._connected = true;
this._channel.connect().catch(() => {
});
return this._server;
}
send(message) {
if ((0, import_rtti.isString)(message))
this._channel.sendToPage({ message, isBase64: false }).catch(() => {
});
else
this._channel.sendToPage({ message: message.toString("base64"), isBase64: true }).catch(() => {
});
}
onMessage(handler) {
this._onPageMessage = handler;
}
onClose(handler) {
this._onPageClose = handler;
}
async [Symbol.asyncDispose]() {
await this.close();
}
async _afterHandle() {
if (this._connected)
return;
await this._channel.ensureOpened().catch(() => {
});
}
}
class WebSocketRouteHandler {
constructor(baseURL, url, handler) {
this._baseURL = baseURL;
this.url = url;
this.handler = handler;
}
static prepareInterceptionPatterns(handlers) {
const patterns = [];
let all = false;
for (const handler of handlers) {
const serialized = (0, import_urlMatch.serializeURLMatch)(handler.url);
if (serialized)
patterns.push(serialized);
else
all = true;
}
if (all)
return [{ glob: "**/*" }];
return patterns;
}
matches(wsURL) {
return (0, import_urlMatch.urlMatches)(this._baseURL, wsURL, this.url, true);
}
async handle(webSocketRoute) {
const handler = this.handler;
await handler(webSocketRoute);
await webSocketRoute._afterHandle();
}
}
class Response extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._finishedPromise = new import_manualPromise.ManualPromise();
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._request = Request.from(this._initializer.request);
this._request._response = this;
Object.assign(this._request._timing, this._initializer.timing);
}
static from(response) {
return response._object;
}
static fromNullable(response) {
return response ? Response.from(response) : null;
}
url() {
return this._initializer.url;
}
ok() {
return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299;
}
status() {
return this._initializer.status;
}
statusText() {
return this._initializer.statusText;
}
fromServiceWorker() {
return this._initializer.fromServiceWorker;
}
/**
* @deprecated
*/
headers() {
return this._provisionalHeaders.headers();
}
async _actualHeaders() {
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = (async () => {
return new RawHeaders((await this._channel.rawResponseHeaders()).headers);
})();
}
return await this._actualHeadersPromise;
}
async allHeaders() {
return (await this._actualHeaders()).headers();
}
async headersArray() {
return (await this._actualHeaders()).headersArray().slice();
}
async headerValue(name) {
return (await this._actualHeaders()).get(name);
}
async headerValues(name) {
return (await this._actualHeaders()).getAll(name);
}
async finished() {
return await this.request()._targetClosedScope().race(this._finishedPromise);
}
async body() {
return (await this._channel.body()).binary;
}
async text() {
const content = await this.body();
return content.toString("utf8");
}
async json() {
const content = await this.text();
return JSON.parse(content);
}
request() {
return this._request;
}
frame() {
return this._request.frame();
}
async serverAddr() {
return (await this._channel.serverAddr()).value || null;
}
async securityDetails() {
return (await this._channel.securityDetails()).value || null;
}
async httpVersion() {
return (await this._channel.httpVersion()).value;
}
}
class WebSocket extends import_channelOwner.ChannelOwner {
static from(webSocket) {
return webSocket._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._isClosed = false;
this._page = parent;
this._channel.on("frameSent", (event) => {
if (event.opcode === 1)
this.emit(import_events.Events.WebSocket.FrameSent, { payload: event.data });
else if (event.opcode === 2)
this.emit(import_events.Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, "base64") });
});
this._channel.on("frameReceived", (event) => {
if (event.opcode === 1)
this.emit(import_events.Events.WebSocket.FrameReceived, { payload: event.data });
else if (event.opcode === 2)
this.emit(import_events.Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, "base64") });
});
this._channel.on("socketError", ({ error }) => this.emit(import_events.Events.WebSocket.Error, error));
this._channel.on("close", () => {
this._isClosed = true;
this.emit(import_events.Events.WebSocket.Close, this);
});
}
url() {
return this._initializer.url;
}
isClosed() {
return this._isClosed;
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== import_events.Events.WebSocket.Error)
waiter.rejectOnEvent(this, import_events.Events.WebSocket.Error, new Error("Socket error"));
if (event !== import_events.Events.WebSocket.Close)
waiter.rejectOnEvent(this, import_events.Events.WebSocket.Close, new Error("Socket closed"));
waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result;
});
}
}
function validateHeaders(headers) {
for (const key of Object.keys(headers)) {
const value = headers[key];
if (!Object.is(value, void 0) && !(0, import_rtti.isString)(value))
throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
}
}
class RouteHandler {
constructor(platform, baseURL, url, handler, times = Number.MAX_SAFE_INTEGER) {
this.handledCount = 0;
this._ignoreException = false;
this._activeInvocations = /* @__PURE__ */ new Set();
this._baseURL = baseURL;
this._times = times;
this.url = url;
this.handler = handler;
this._savedZone = platform.zones.current().pop();
}
static prepareInterceptionPatterns(handlers) {
const patterns = [];
let all = false;
for (const handler of handlers) {
const serialized = (0, import_urlMatch.serializeURLMatch)(handler.url);
if (serialized)
patterns.push(serialized);
else
all = true;
}
if (all)
return [{ glob: "**/*" }];
return patterns;
}
matches(requestURL) {
return (0, import_urlMatch.urlMatches)(this._baseURL, requestURL, this.url);
}
async handle(route) {
return await this._savedZone.run(async () => this._handleImpl(route));
}
async _handleImpl(route) {
const handlerInvocation = { complete: new import_manualPromise.ManualPromise(), route };
this._activeInvocations.add(handlerInvocation);
try {
return await this._handleInternal(route);
} catch (e) {
if (this._ignoreException)
return false;
if ((0, import_errors.isTargetClosedError)(e)) {
(0, import_stackTrace.rewriteErrorMessage)(e, `"${e.message}" while running route callback.
Consider awaiting \`await page.unrouteAll({ behavior: 'ignoreErrors' })\`
before the end of the test to ignore remaining routes in flight.`);
}
throw e;
} finally {
handlerInvocation.complete.resolve();
this._activeInvocations.delete(handlerInvocation);
}
}
async stop(behavior) {
if (behavior === "ignoreErrors") {
this._ignoreException = true;
} else {
const promises = [];
for (const activation of this._activeInvocations) {
if (!activation.route._didThrow)
promises.push(activation.complete);
}
await Promise.all(promises);
}
}
async _handleInternal(route) {
++this.handledCount;
const handledPromise = route._startHandling();
const handler = this.handler;
const [handled] = await Promise.all([
handledPromise,
handler(route, route.request())
]);
return handled;
}
willExpire() {
return this.handledCount + 1 >= this._times;
}
}
class RawHeaders {
constructor(headers) {
this._headersMap = new import_multimap.MultiMap();
this._headersArray = headers;
for (const header of headers)
this._headersMap.set(header.name.toLowerCase(), header.value);
}
static _fromHeadersObjectLossy(headers) {
const headersArray = Object.entries(headers).map(([name, value]) => ({
name,
value
})).filter((header) => header.value !== void 0);
return new RawHeaders(headersArray);
}
get(name) {
const values = this.getAll(name);
if (!values || !values.length)
return null;
return values.join(name.toLowerCase() === "set-cookie" ? "\n" : ", ");
}
getAll(name) {
return [...this._headersMap.get(name.toLowerCase())];
}
headers() {
const result = {};
for (const name of this._headersMap.keys())
result[name] = this.get(name);
return result;
}
headersArray() {
return this._headersArray;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
RawHeaders,
Request,
Response,
Route,
RouteHandler,
WebSocket,
WebSocketRoute,
WebSocketRouteHandler,
validateHeaders
});
+731
View File
@@ -0,0 +1,731 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var page_exports = {};
__export(page_exports, {
BindingCall: () => BindingCall,
Page: () => Page
});
module.exports = __toCommonJS(page_exports);
var import_artifact = require("./artifact");
var import_channelOwner = require("./channelOwner");
var import_clientHelper = require("./clientHelper");
var import_coverage = require("./coverage");
var import_disposable = require("./disposable");
var import_download = require("./download");
var import_elementHandle = require("./elementHandle");
var import_errors = require("./errors");
var import_events = require("./events");
var import_fileChooser = require("./fileChooser");
var import_frame = require("./frame");
var import_harRouter = require("./harRouter");
var import_input = require("./input");
var import_jsHandle = require("./jsHandle");
var import_network = require("./network");
var import_video = require("./video");
var import_screencast = require("./screencast");
var import_waiter = require("./waiter");
var import_worker = require("./worker");
var import_timeoutSettings = require("./timeoutSettings");
var import_assert = require("../utils/isomorphic/assert");
var import_fileUtils = require("./fileUtils");
var import_headers = require("../utils/isomorphic/headers");
var import_stringUtils = require("../utils/isomorphic/stringUtils");
var import_urlMatch = require("../utils/isomorphic/urlMatch");
var import_manualPromise = require("../utils/isomorphic/manualPromise");
var import_rtti = require("../utils/isomorphic/rtti");
var import_consoleMessage = require("./consoleMessage");
class Page extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._frames = /* @__PURE__ */ new Set();
this._workers = /* @__PURE__ */ new Set();
this._closed = false;
this._closedOrCrashedScope = new import_manualPromise.LongStandingScope();
this._routes = [];
this._webSocketRoutes = [];
this._bindings = /* @__PURE__ */ new Map();
this._closeWasCalled = false;
this._harRouters = [];
this._locatorHandlers = /* @__PURE__ */ new Map();
this._instrumentation.onPage(this);
this._browserContext = parent;
this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
this.keyboard = new import_input.Keyboard(this);
this.mouse = new import_input.Mouse(this);
this.request = this._browserContext.request;
this.touchscreen = new import_input.Touchscreen(this);
this.clock = this._browserContext.clock;
this._mainFrame = import_frame.Frame.from(initializer.mainFrame);
this._mainFrame._page = this;
this._frames.add(this._mainFrame);
this._viewportSize = initializer.viewportSize;
this._closed = initializer.isClosed;
this._opener = Page.fromNullable(initializer.opener);
this._video = new import_video.Video(this, this._connection, initializer.video ? import_artifact.Artifact.from(initializer.video) : void 0);
this.screencast = new import_screencast.Screencast(this);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("crash", () => this._onCrash());
this._channel.on("download", ({ url, suggestedFilename, artifact }) => {
const artifactObject = import_artifact.Artifact.from(artifact);
this.emit(import_events.Events.Page.Download, new import_download.Download(this, url, suggestedFilename, artifactObject));
});
this._channel.on("fileChooser", ({ element, isMultiple }) => this.emit(import_events.Events.Page.FileChooser, new import_fileChooser.FileChooser(this, import_elementHandle.ElementHandle.from(element), isMultiple)));
this._channel.on("frameAttached", ({ frame }) => this._onFrameAttached(import_frame.Frame.from(frame)));
this._channel.on("frameDetached", ({ frame }) => this._onFrameDetached(import_frame.Frame.from(frame)));
this._channel.on("locatorHandlerTriggered", ({ uid }) => this._onLocatorHandlerTriggered(uid));
this._channel.on("route", ({ route }) => this._onRoute(import_network.Route.from(route)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(import_network.WebSocketRoute.from(webSocketRoute)));
this._channel.on("viewportSizeChanged", ({ viewportSize }) => this._viewportSize = viewportSize);
this._channel.on("webSocket", ({ webSocket }) => this.emit(import_events.Events.Page.WebSocket, import_network.WebSocket.from(webSocket)));
this._channel.on("worker", ({ worker }) => this._onWorker(import_worker.Worker.from(worker)));
this.coverage = new import_coverage.Coverage(this._channel);
this.once(import_events.Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason()));
this.once(import_events.Events.Page.Crash, () => this._closedOrCrashedScope.close(new import_errors.TargetClosedError()));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.Page.Console, "console"],
[import_events.Events.Page.Dialog, "dialog"],
[import_events.Events.Page.Request, "request"],
[import_events.Events.Page.Response, "response"],
[import_events.Events.Page.RequestFinished, "requestFinished"],
[import_events.Events.Page.RequestFailed, "requestFailed"],
[import_events.Events.Page.FileChooser, "fileChooser"]
]));
}
static from(page) {
return page._object;
}
static fromNullable(page) {
return page ? Page.from(page) : null;
}
_onFrameAttached(frame) {
frame._page = this;
this._frames.add(frame);
if (frame._parentFrame)
frame._parentFrame._childFrames.add(frame);
this.emit(import_events.Events.Page.FrameAttached, frame);
}
_onFrameDetached(frame) {
this._frames.delete(frame);
frame._detached = true;
if (frame._parentFrame)
frame._parentFrame._childFrames.delete(frame);
this.emit(import_events.Events.Page.FrameDetached, frame);
}
async _onRoute(route) {
route._context = this.context();
const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) {
if (this._closeWasCalled || this._browserContext.isClosed())
return;
if (!routeHandler.matches(route.request().url()))
continue;
const index = this._routes.indexOf(routeHandler);
if (index === -1)
continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route);
if (!this._routes.length)
this._updateInterceptionPatterns({ internal: true }).catch(() => {
});
if (handled)
return;
}
await this._browserContext._onRoute(route);
}
async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find((route) => route.matches(webSocketRoute.url()));
if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
await this._browserContext._onWebSocketRoute(webSocketRoute);
}
async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name);
if (func) {
await bindingCall.call(func);
return;
}
await this._browserContext._onBinding(bindingCall);
}
_onWorker(worker) {
this._workers.add(worker);
worker._page = this;
this.emit(import_events.Events.Page.Worker, worker);
}
_onClose() {
this._closed = true;
this._browserContext._pages.delete(this);
this._disposeHarRouters();
this.emit(import_events.Events.Page.Close, this);
}
_onCrash() {
this.emit(import_events.Events.Page.Crash, this);
}
context() {
return this._browserContext;
}
async opener() {
if (!this._opener || this._opener.isClosed())
return null;
return this._opener;
}
mainFrame() {
return this._mainFrame;
}
frame(frameSelector) {
const name = (0, import_rtti.isString)(frameSelector) ? frameSelector : frameSelector.name;
const url = (0, import_rtti.isObject)(frameSelector) ? frameSelector.url : void 0;
(0, import_assert.assert)(name || url, "Either name or url matcher should be specified");
return this.frames().find((f) => {
if (name)
return f.name() === name;
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, f.url(), url);
}) || null;
}
frames() {
return [...this._frames];
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
video() {
if (!this._browserContext._options.recordVideo)
return null;
return this._video;
}
async pickLocator() {
const { selector } = await this._channel.pickLocator({});
return this.locator(selector);
}
async cancelPickLocator() {
await this._channel.cancelPickLocator({});
}
async $(selector, options) {
return await this._mainFrame.$(selector, options);
}
async waitForSelector(selector, options) {
return await this._mainFrame.waitForSelector(selector, options);
}
async dispatchEvent(selector, type, eventInit, options) {
return await this._mainFrame.dispatchEvent(selector, type, eventInit, options);
}
async evaluateHandle(pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
return await this._mainFrame.evaluateHandle(pageFunction, arg);
}
async $eval(selector, pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
return await this._mainFrame.$eval(selector, pageFunction, arg);
}
async $$eval(selector, pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
return await this._mainFrame.$$eval(selector, pageFunction, arg);
}
async $$(selector) {
return await this._mainFrame.$$(selector);
}
async addScriptTag(options = {}) {
return await this._mainFrame.addScriptTag(options);
}
async addStyleTag(options = {}) {
return await this._mainFrame.addStyleTag(options);
}
async exposeFunction(name, callback) {
const result = await this._channel.exposeBinding({ name });
const binding = (source, ...args) => callback(...args);
this._bindings.set(name, binding);
return import_disposable.DisposableObject.from(result.disposable);
}
async exposeBinding(name, callback, options = {}) {
const result = await this._channel.exposeBinding({ name, needsHandle: options.handle });
this._bindings.set(name, callback);
return import_disposable.DisposableObject.from(result.disposable);
}
async setExtraHTTPHeaders(headers) {
(0, import_network.validateHeaders)(headers);
await this._channel.setExtraHTTPHeaders({ headers: (0, import_headers.headersObjectToArray)(headers) });
}
url() {
return this._mainFrame.url();
}
async content() {
return await this._mainFrame.content();
}
async setContent(html, options) {
return await this._mainFrame.setContent(html, options);
}
async goto(url, options) {
return await this._mainFrame.goto(url, options);
}
async reload(options = {}) {
const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return import_network.Response.fromNullable((await this._channel.reload({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response);
}
async addLocatorHandler(locator, handler, options = {}) {
if (locator._frame !== this._mainFrame)
throw new Error(`Locator must belong to the main frame of this page`);
if (options.times === 0)
return;
const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, noWaitAfter: options.noWaitAfter });
this._locatorHandlers.set(uid, { locator, handler, times: options.times });
}
async _onLocatorHandlerTriggered(uid) {
let remove = false;
try {
const handler = this._locatorHandlers.get(uid);
if (handler && handler.times !== 0) {
if (handler.times !== void 0)
handler.times--;
await handler.handler(handler.locator);
}
remove = handler?.times === 0;
} finally {
if (remove)
this._locatorHandlers.delete(uid);
this._channel.resolveLocatorHandlerNoReply({ uid, remove }).catch(() => {
});
}
}
async removeLocatorHandler(locator) {
for (const [uid, data] of this._locatorHandlers) {
if (data.locator._equals(locator)) {
this._locatorHandlers.delete(uid);
await this._channel.unregisterLocatorHandler({ uid }).catch(() => {
});
}
}
}
async waitForLoadState(state, options) {
return await this._mainFrame.waitForLoadState(state, options);
}
async waitForNavigation(options) {
return await this._mainFrame.waitForNavigation(options);
}
async waitForURL(url, options) {
return await this._mainFrame.waitForURL(url, options);
}
async waitForRequest(urlOrPredicate, options = {}) {
const predicate = async (request) => {
if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, request.url(), urlOrPredicate);
return await urlOrPredicate(request);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : void 0;
return await this._waitForEvent(import_events.Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
}
async waitForResponse(urlOrPredicate, options = {}) {
const predicate = async (response) => {
if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, response.url(), urlOrPredicate);
return await urlOrPredicate(response);
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : void 0;
return await this._waitForEvent(import_events.Events.Page.Response, { predicate, timeout: options.timeout }, logLine);
}
async waitForEvent(event, optionsOrPredicate = {}) {
return await this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`);
}
_closeErrorWithReason() {
return new import_errors.TargetClosedError(this._closeReason || this._browserContext._effectiveCloseReason());
}
async _waitForEvent(event, optionsOrPredicate, logLine) {
return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = import_waiter.Waiter.createForEvent(this, event);
if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== import_events.Events.Page.Crash)
waiter.rejectOnEvent(this, import_events.Events.Page.Crash, new Error("Page crashed"));
if (event !== import_events.Events.Page.Close)
waiter.rejectOnEvent(this, import_events.Events.Page.Close, () => this._closeErrorWithReason());
const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose();
return result;
});
}
async goBack(options = {}) {
const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return import_network.Response.fromNullable((await this._channel.goBack({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response);
}
async goForward(options = {}) {
const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return import_network.Response.fromNullable((await this._channel.goForward({ ...options, waitUntil, timeout: this._timeoutSettings.navigationTimeout(options) })).response);
}
async requestGC() {
await this._channel.requestGC();
}
async emulateMedia(options = {}) {
await this._channel.emulateMedia({
media: options.media === null ? "no-override" : options.media,
colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme,
reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion,
forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors,
contrast: options.contrast === null ? "no-override" : options.contrast
});
}
async setViewportSize(viewportSize) {
this._viewportSize = viewportSize;
await this._channel.setViewportSize({ viewportSize });
}
viewportSize() {
return this._viewportSize || null;
}
async evaluate(pageFunction, arg) {
(0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
return await this._mainFrame.evaluate(pageFunction, arg);
}
async addInitScript(script, arg) {
const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
return import_disposable.DisposableObject.from((await this._channel.addInitScript({ source })).disposable);
}
async route(url, handler, options = {}) {
this._routes.unshift(new import_network.RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times));
await this._updateInterceptionPatterns({ title: "Route requests" });
return new import_disposable.DisposableStub(() => this.unroute(url, handler));
}
async routeFromHAR(har, options = {}) {
const localUtils = this._connection.localUtils();
if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options.update) {
await this._browserContext._recordIntoHAR(har, this, options);
return;
}
const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
this._harRouters.push(harRouter);
await harRouter.addPageRoute(this);
}
async routeWebSocket(url, handler) {
this._webSocketRoutes.unshift(new import_network.WebSocketRouteHandler(this._browserContext._options.baseURL, url, handler));
await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
}
_disposeHarRouters() {
this._harRouters.forEach((router) => router.dispose());
this._harRouters = [];
}
async unrouteAll(options) {
await this._unrouteInternal(this._routes, [], options?.behavior);
this._disposeHarRouters();
}
async unroute(url, handler) {
const removed = [];
const remaining = [];
for (const route of this._routes) {
if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
removed.push(route);
else
remaining.push(route);
}
await this._unrouteInternal(removed, remaining, "default");
}
async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining;
if (behavior && behavior !== "default") {
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises);
}
await this._updateInterceptionPatterns({ title: "Unroute requests" });
}
async _updateInterceptionPatterns(options) {
const patterns = import_network.RouteHandler.prepareInterceptionPatterns(this._routes);
await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options);
}
async _updateWebSocketInterceptionPatterns(options) {
const patterns = import_network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options);
}
async screenshot(options = {}) {
const mask = options.mask;
const copy = { ...options, mask: void 0, timeout: this._timeoutSettings.timeout(options) };
if (!copy.type)
copy.type = (0, import_elementHandle.determineScreenshotType)(options);
if (mask) {
copy.mask = mask.map((locator) => ({
frame: locator._frame._channel,
selector: locator._selector
}));
}
const result = await this._channel.screenshot(copy);
if (options.path) {
await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, result.binary);
}
return result.binary;
}
async _expectScreenshot(options) {
const mask = options?.mask ? options?.mask.map((locator2) => ({
frame: locator2._frame._channel,
selector: locator2._selector
})) : void 0;
const locator = options.locator ? {
frame: options.locator._frame._channel,
selector: options.locator._selector
} : void 0;
return await this._channel.expectScreenshot({
...options,
isNot: !!options.isNot,
locator,
mask
});
}
async title() {
return await this._mainFrame.title();
}
async bringToFront() {
await this._channel.bringToFront();
}
async [Symbol.asyncDispose]() {
await this.close();
}
async close(options = {}) {
this._closeReason = options.reason;
if (!options.runBeforeUnload)
this._closeWasCalled = true;
try {
if (this._ownedContext)
await this._ownedContext.close();
else
await this._channel.close(options);
} catch (e) {
if ((0, import_errors.isTargetClosedError)(e) && !options.runBeforeUnload)
return;
throw e;
}
}
isClosed() {
return this._closed;
}
async click(selector, options) {
return await this._mainFrame.click(selector, options);
}
async dragAndDrop(source, target, options) {
return await this._mainFrame.dragAndDrop(source, target, options);
}
async dblclick(selector, options) {
await this._mainFrame.dblclick(selector, options);
}
async tap(selector, options) {
return await this._mainFrame.tap(selector, options);
}
async fill(selector, value, options) {
return await this._mainFrame.fill(selector, value, options);
}
async clearConsoleMessages() {
await this._channel.clearConsoleMessages();
}
async consoleMessages(options) {
const { messages } = await this._channel.consoleMessages({ filter: options?.filter });
return messages.map((message) => new import_consoleMessage.ConsoleMessage(this._platform, message, this, null));
}
async clearPageErrors() {
await this._channel.clearPageErrors();
}
async pageErrors(options) {
const { errors } = await this._channel.pageErrors({ filter: options?.filter });
return errors.map((error) => (0, import_errors.parseError)(error));
}
locator(selector, options) {
return this.mainFrame().locator(selector, options);
}
getByTestId(testId) {
return this.mainFrame().getByTestId(testId);
}
getByAltText(text, options) {
return this.mainFrame().getByAltText(text, options);
}
getByLabel(text, options) {
return this.mainFrame().getByLabel(text, options);
}
getByPlaceholder(text, options) {
return this.mainFrame().getByPlaceholder(text, options);
}
getByText(text, options) {
return this.mainFrame().getByText(text, options);
}
getByTitle(text, options) {
return this.mainFrame().getByTitle(text, options);
}
getByRole(role, options = {}) {
return this.mainFrame().getByRole(role, options);
}
frameLocator(selector) {
return this.mainFrame().frameLocator(selector);
}
async focus(selector, options) {
return await this._mainFrame.focus(selector, options);
}
async textContent(selector, options) {
return await this._mainFrame.textContent(selector, options);
}
async innerText(selector, options) {
return await this._mainFrame.innerText(selector, options);
}
async innerHTML(selector, options) {
return await this._mainFrame.innerHTML(selector, options);
}
async getAttribute(selector, name, options) {
return await this._mainFrame.getAttribute(selector, name, options);
}
async inputValue(selector, options) {
return await this._mainFrame.inputValue(selector, options);
}
async isChecked(selector, options) {
return await this._mainFrame.isChecked(selector, options);
}
async isDisabled(selector, options) {
return await this._mainFrame.isDisabled(selector, options);
}
async isEditable(selector, options) {
return await this._mainFrame.isEditable(selector, options);
}
async isEnabled(selector, options) {
return await this._mainFrame.isEnabled(selector, options);
}
async isHidden(selector, options) {
return await this._mainFrame.isHidden(selector, options);
}
async isVisible(selector, options) {
return await this._mainFrame.isVisible(selector, options);
}
async hover(selector, options) {
return await this._mainFrame.hover(selector, options);
}
async selectOption(selector, values, options) {
return await this._mainFrame.selectOption(selector, values, options);
}
async setInputFiles(selector, files, options) {
return await this._mainFrame.setInputFiles(selector, files, options);
}
async type(selector, text, options) {
return await this._mainFrame.type(selector, text, options);
}
async press(selector, key, options) {
return await this._mainFrame.press(selector, key, options);
}
async check(selector, options) {
return await this._mainFrame.check(selector, options);
}
async uncheck(selector, options) {
return await this._mainFrame.uncheck(selector, options);
}
async setChecked(selector, checked, options) {
return await this._mainFrame.setChecked(selector, checked, options);
}
async waitForTimeout(timeout) {
return await this._mainFrame.waitForTimeout(timeout);
}
async waitForFunction(pageFunction, arg, options) {
return await this._mainFrame.waitForFunction(pageFunction, arg, options);
}
async requests() {
const { requests } = await this._channel.requests();
return requests.map((request) => import_network.Request.from(request));
}
workers() {
return [...this._workers];
}
async pause(_options) {
if (this._platform.isJSDebuggerAttached())
return;
const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout();
const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout();
this._browserContext.setDefaultNavigationTimeout(0);
this._browserContext.setDefaultTimeout(0);
this._instrumentation?.onWillPause({ keepTestTimeout: !!_options?.__testHookKeepTestTimeout });
await this._closedOrCrashedScope.safeRace(this.context()._channel.pause());
this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout);
this._browserContext.setDefaultTimeout(defaultTimeout);
}
async pdf(options = {}) {
const transportOptions = { ...options };
if (transportOptions.margin)
transportOptions.margin = { ...transportOptions.margin };
if (typeof options.width === "number")
transportOptions.width = options.width + "px";
if (typeof options.height === "number")
transportOptions.height = options.height + "px";
for (const margin of ["top", "right", "bottom", "left"]) {
const index = margin;
if (options.margin && typeof options.margin[index] === "number")
transportOptions.margin[index] = transportOptions.margin[index] + "px";
}
const result = await this._channel.pdf(transportOptions);
if (options.path) {
const platform = this._platform;
await platform.fs().promises.mkdir(platform.path().dirname(options.path), { recursive: true });
await platform.fs().promises.writeFile(options.path, result.pdf);
}
return result.pdf;
}
async ariaSnapshot(options = {}) {
const result = await this.mainFrame()._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options), track: options._track, mode: options.mode, depth: options.depth });
return result.snapshot;
}
async _setDockTile(image) {
await this._channel.setDockTile({ image });
}
}
class BindingCall extends import_channelOwner.ChannelOwner {
static from(channel) {
return channel._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
}
async call(func) {
try {
const frame = import_frame.Frame.from(this._initializer.frame);
const source = {
context: frame._page.context(),
page: frame._page,
frame
};
let result;
if (this._initializer.handle)
result = await func(source, import_jsHandle.JSHandle.from(this._initializer.handle));
else
result = await func(source, ...this._initializer.args.map(import_jsHandle.parseResult));
this._channel.resolve({ result: (0, import_jsHandle.serializeArgument)(result) }).catch(() => {
});
} catch (e) {
this._channel.reject({ error: (0, import_errors.serializeError)(e) }).catch(() => {
});
}
}
}
function trimUrl(param) {
if ((0, import_rtti.isRegExp)(param))
return `/${(0, import_stringUtils.trimStringWithEllipsis)(param.source, 50)}/${param.flags}`;
if ((0, import_rtti.isString)(param))
return `"${(0, import_stringUtils.trimStringWithEllipsis)(param, 50)}"`;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BindingCall,
Page
});
+74
View File
@@ -0,0 +1,74 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var platform_exports = {};
__export(platform_exports, {
emptyPlatform: () => emptyPlatform
});
module.exports = __toCommonJS(platform_exports);
var import_colors = require("../utils/isomorphic/colors");
const noopZone = {
push: () => noopZone,
pop: () => noopZone,
run: (func) => func(),
data: () => void 0
};
const emptyPlatform = {
name: "empty",
boxedStackPrefixes: () => [],
calculateSha1: async () => {
throw new Error("Not implemented");
},
colors: import_colors.webColors,
createGuid: () => {
throw new Error("Not implemented");
},
defaultMaxListeners: () => 10,
env: {},
fs: () => {
throw new Error("Not implemented");
},
inspectCustom: void 0,
isDebugMode: () => false,
isJSDebuggerAttached: () => false,
isLogEnabled(name) {
return false;
},
isUnderTest: () => false,
log(name, message) {
},
path: () => {
throw new Error("Function not implemented.");
},
pathSeparator: "/",
showInternalStackFrames: () => false,
streamFile(path, writable) {
throw new Error("Streams are not available");
},
streamReadable: (channel) => {
throw new Error("Streams are not available");
},
streamWritable: (channel) => {
throw new Error("Streams are not available");
},
zones: { empty: noopZone, current: () => noopZone }
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
emptyPlatform
});
+71
View File
@@ -0,0 +1,71 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var playwright_exports = {};
__export(playwright_exports, {
Playwright: () => Playwright
});
module.exports = __toCommonJS(playwright_exports);
var import_android = require("./android");
var import_browser = require("./browser");
var import_browserType = require("./browserType");
var import_channelOwner = require("./channelOwner");
var import_electron = require("./electron");
var import_errors = require("./errors");
var import_fetch = require("./fetch");
var import_selectors = require("./selectors");
class Playwright extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this.request = new import_fetch.APIRequest(this);
this.chromium = import_browserType.BrowserType.from(initializer.chromium);
this.chromium._playwright = this;
this.firefox = import_browserType.BrowserType.from(initializer.firefox);
this.firefox._playwright = this;
this.webkit = import_browserType.BrowserType.from(initializer.webkit);
this.webkit._playwright = this;
this._android = import_android.Android.from(initializer.android);
this._android._playwright = this;
this._electron = import_electron.Electron.from(initializer.electron);
this._electron._playwright = this;
this.devices = this._connection.localUtils()?.devices ?? {};
this.selectors = new import_selectors.Selectors(this._connection._platform);
this.errors = { TimeoutError: import_errors.TimeoutError };
}
static from(channel) {
return channel._object;
}
_browserTypes() {
return [this.chromium, this.firefox, this.webkit];
}
_preLaunchedBrowser() {
const browser = import_browser.Browser.from(this._initializer.preLaunchedBrowser);
browser._connectToBrowserType(this[browser._name], {}, void 0);
return browser;
}
_allContexts() {
return this._browserTypes().flatMap((type) => [...type._contexts]);
}
_allPages() {
return this._allContexts().flatMap((context) => context.pages());
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Playwright
});
+88
View File
@@ -0,0 +1,88 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var screencast_exports = {};
__export(screencast_exports, {
Screencast: () => Screencast
});
module.exports = __toCommonJS(screencast_exports);
var import_artifact = require("./artifact");
var import_disposable = require("./disposable");
class Screencast {
constructor(page) {
this._started = false;
this._onFrame = null;
this._page = page;
this._page._channel.on("screencastFrame", ({ data }) => {
void this._onFrame?.({ data });
});
}
async start(options = {}) {
if (this._started)
throw new Error("Screencast is already started");
this._started = true;
if (options.onFrame)
this._onFrame = options.onFrame;
const result = await this._page._channel.screencastStart({
size: options.size,
quality: options.quality,
sendFrames: !!options.onFrame,
record: !!options.path
});
if (result.artifact) {
this._artifact = import_artifact.Artifact.from(result.artifact);
this._savePath = options.path;
}
return new import_disposable.DisposableStub(() => this.stop());
}
async stop() {
await this._page._wrapApiCall(async () => {
this._started = false;
this._onFrame = null;
await this._page._channel.screencastStop();
if (this._savePath)
await this._artifact?.saveAs(this._savePath);
this._artifact = void 0;
this._savePath = void 0;
});
}
async showActions(options) {
await this._page._channel.screencastShowActions({ duration: options?.duration, position: options?.position, fontSize: options?.fontSize });
return new import_disposable.DisposableStub(() => this._page._channel.screencastHideActions());
}
async hideActions() {
await this._page._channel.screencastHideActions();
}
async showOverlay(html, options) {
const { id } = await this._page._channel.screencastShowOverlay({ html, duration: options?.duration });
return new import_disposable.DisposableStub(() => this._page._channel.screencastRemoveOverlay({ id }));
}
async showChapter(title, options) {
await this._page._channel.screencastChapter({ title, ...options });
}
async showOverlays() {
await this._page._channel.screencastSetOverlayVisible({ visible: true });
}
async hideOverlays() {
await this._page._channel.screencastSetOverlayVisible({ visible: false });
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Screencast
});
+57
View File
@@ -0,0 +1,57 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var selectors_exports = {};
__export(selectors_exports, {
Selectors: () => Selectors
});
module.exports = __toCommonJS(selectors_exports);
var import_clientHelper = require("./clientHelper");
var import_locator = require("./locator");
class Selectors {
constructor(platform) {
this._selectorEngines = [];
this._contextsForSelectors = /* @__PURE__ */ new Set();
this._platform = platform;
}
async register(name, script, options = {}) {
if (this._selectorEngines.some((engine) => engine.name === name))
throw new Error(`selectors.register: "${name}" selector engine has been already registered`);
const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, void 0, false);
const selectorEngine = { ...options, name, source };
for (const context of this._contextsForSelectors)
await context._channel.registerSelectorEngine({ selectorEngine });
this._selectorEngines.push(selectorEngine);
}
setTestIdAttribute(attributeName) {
this._testIdAttributeName = attributeName;
(0, import_locator.setTestIdAttribute)(attributeName);
for (const context of this._contextsForSelectors) {
context._options.testIdAttributeName = attributeName;
context._channel.setTestIdAttributeName({ testIdAttributeName: attributeName }).catch(() => {
});
}
}
_withSelectorOptions(options) {
return { ...options, selectorEngines: this._selectorEngines, testIdAttributeName: this._testIdAttributeName };
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Selectors
});
+39
View File
@@ -0,0 +1,39 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var stream_exports = {};
__export(stream_exports, {
Stream: () => Stream
});
module.exports = __toCommonJS(stream_exports);
var import_channelOwner = require("./channelOwner");
class Stream extends import_channelOwner.ChannelOwner {
static from(Stream2) {
return Stream2._object;
}
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
}
stream() {
return this._platform.streamReadable(this._channel);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Stream
});
+79
View File
@@ -0,0 +1,79 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var timeoutSettings_exports = {};
__export(timeoutSettings_exports, {
TimeoutSettings: () => TimeoutSettings
});
module.exports = __toCommonJS(timeoutSettings_exports);
var import_time = require("../utils/isomorphic/time");
class TimeoutSettings {
constructor(platform, parent) {
this._parent = parent;
this._platform = platform;
}
setDefaultTimeout(timeout) {
this._defaultTimeout = timeout;
}
setDefaultNavigationTimeout(timeout) {
this._defaultNavigationTimeout = timeout;
}
defaultNavigationTimeout() {
return this._defaultNavigationTimeout;
}
defaultTimeout() {
return this._defaultTimeout;
}
navigationTimeout(options) {
if (typeof options.timeout === "number")
return options.timeout;
if (this._defaultNavigationTimeout !== void 0)
return this._defaultNavigationTimeout;
if (this._platform.isDebugMode())
return 0;
if (this._defaultTimeout !== void 0)
return this._defaultTimeout;
if (this._parent)
return this._parent.navigationTimeout(options);
return import_time.DEFAULT_PLAYWRIGHT_TIMEOUT;
}
timeout(options) {
if (typeof options.timeout === "number")
return options.timeout;
if (this._platform.isDebugMode())
return 0;
if (this._defaultTimeout !== void 0)
return this._defaultTimeout;
if (this._parent)
return this._parent.timeout(options);
return import_time.DEFAULT_PLAYWRIGHT_TIMEOUT;
}
launchTimeout(options) {
if (typeof options.timeout === "number")
return options.timeout;
if (this._platform.isDebugMode())
return 0;
if (this._parent)
return this._parent.launchTimeout(options);
return import_time.DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
TimeoutSettings
});

Some files were not shown because too many files have changed in this diff Show More