Harden mobile smoke and contract ownership rules
This commit is contained in:
@@ -39,12 +39,15 @@ class GeneratedContractRepository:
|
||||
tenant_id: str,
|
||||
page: int,
|
||||
page_size: int,
|
||||
generated_by: Optional[str] = None,
|
||||
contract_type: Optional[str] = None,
|
||||
document_type_code: Optional[str] = None,
|
||||
lifecycle_status: Optional[str] = None,
|
||||
keyword: Optional[str] = None,
|
||||
) -> List[GeneratedContract]:
|
||||
query = self.db.query(GeneratedContract).filter(GeneratedContract.tenant_id == tenant_id)
|
||||
if generated_by:
|
||||
query = query.filter(GeneratedContract.generated_by == generated_by)
|
||||
if contract_type:
|
||||
query = query.filter(GeneratedContract.contract_type == contract_type)
|
||||
if document_type_code:
|
||||
@@ -67,12 +70,15 @@ class GeneratedContractRepository:
|
||||
def count(
|
||||
self,
|
||||
tenant_id: str,
|
||||
generated_by: Optional[str] = None,
|
||||
contract_type: Optional[str] = None,
|
||||
document_type_code: Optional[str] = None,
|
||||
lifecycle_status: Optional[str] = None,
|
||||
keyword: Optional[str] = None,
|
||||
) -> int:
|
||||
query = self.db.query(GeneratedContract).filter(GeneratedContract.tenant_id == tenant_id)
|
||||
if generated_by:
|
||||
query = query.filter(GeneratedContract.generated_by == generated_by)
|
||||
if contract_type:
|
||||
query = query.filter(GeneratedContract.contract_type == contract_type)
|
||||
if document_type_code:
|
||||
|
||||
@@ -452,11 +452,13 @@ class ContractGenerationService:
|
||||
keyword: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_by = None if current_user.get("role") == "admin" else str(current_user.get("id") or "")
|
||||
normalized_lifecycle_status = self._normalize_optional_lifecycle_status(lifecycle_status)
|
||||
items = self.generated_contract_repository.list_paginated(
|
||||
tenant_id=tenant_id,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
generated_by=generated_by,
|
||||
contract_type=contract_type,
|
||||
document_type_code=document_type_code,
|
||||
lifecycle_status=normalized_lifecycle_status,
|
||||
@@ -464,6 +466,7 @@ class ContractGenerationService:
|
||||
)
|
||||
total = self.generated_contract_repository.count(
|
||||
tenant_id=tenant_id,
|
||||
generated_by=generated_by,
|
||||
contract_type=contract_type,
|
||||
document_type_code=document_type_code,
|
||||
lifecycle_status=normalized_lifecycle_status,
|
||||
@@ -478,8 +481,7 @@ class ContractGenerationService:
|
||||
|
||||
|
||||
def get_generated_contract(self, current_user: Dict[str, Any], contract_id: str) -> Dict:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="合同不存在或无权访问")
|
||||
return self._to_generated_contract_item(generated_contract)
|
||||
@@ -497,7 +499,7 @@ class ContractGenerationService:
|
||||
lifecycle_note: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="contract not found")
|
||||
if current_user.get("role") != "admin" and generated_contract.generated_by != current_user.get("id"):
|
||||
@@ -723,7 +725,7 @@ class ContractGenerationService:
|
||||
|
||||
def list_generated_contract_versions(self, current_user: Dict[str, Any], contract_id: str) -> Dict[str, Any]:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="contract not found")
|
||||
versions = self.generated_contract_version_repository.list_by_contract(tenant_id, contract_id)
|
||||
@@ -766,7 +768,7 @@ class ContractGenerationService:
|
||||
|
||||
def get_generated_contract_version(self, current_user: Dict[str, Any], contract_id: str, version_id: str) -> Dict[str, Any]:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="contract not found")
|
||||
version = self.generated_contract_version_repository.get_by_id(tenant_id, contract_id, version_id)
|
||||
@@ -833,11 +835,7 @@ class ContractGenerationService:
|
||||
locate_status: str = "applied",
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="contract not found")
|
||||
if current_user.get("role") != "admin" and generated_contract.generated_by != current_user.get("id"):
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="contract not found")
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
|
||||
normalized_content = str(content or "").strip()
|
||||
if not normalized_content:
|
||||
@@ -875,7 +873,7 @@ class ContractGenerationService:
|
||||
|
||||
def get_generated_contract_preview(self, current_user: Dict[str, Any], contract_id: str) -> Dict:
|
||||
tenant_id = current_user["tenant_id"]
|
||||
generated_contract = self.generated_contract_repository.get_by_id(contract_id, tenant_id)
|
||||
generated_contract = self._get_generated_contract_for_lifecycle_task(current_user, contract_id)
|
||||
if not generated_contract:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="合同不存在或无权访问")
|
||||
return {
|
||||
|
||||
@@ -56,7 +56,15 @@ class StubContractGenerationService:
|
||||
"chat_url": f"/chat?review={review_id}",
|
||||
}
|
||||
|
||||
def _record(self, record_id, tenant_id="tenant-demo-001", contract_type="procurement", document_type_code="procurement_goods"):
|
||||
def _record(
|
||||
self,
|
||||
record_id,
|
||||
tenant_id="tenant-demo-001",
|
||||
contract_type="procurement",
|
||||
document_type_code="procurement_goods",
|
||||
generated_by="user-admin",
|
||||
generated_by_name="Admin",
|
||||
):
|
||||
return {
|
||||
"id": record_id,
|
||||
"template_id": "tpl-123",
|
||||
@@ -90,8 +98,8 @@ class StubContractGenerationService:
|
||||
"updated_at": None,
|
||||
},
|
||||
"metadata": {
|
||||
"generated_by": "user-admin",
|
||||
"generated_by_name": "Admin",
|
||||
"generated_by": generated_by,
|
||||
"generated_by_name": generated_by_name,
|
||||
"tenant_id": tenant_id,
|
||||
"original_template": "Procurement Template",
|
||||
"document_type_code": document_type_code,
|
||||
@@ -109,9 +117,21 @@ class StubContractGenerationService:
|
||||
records = [
|
||||
self._record("contract-123"),
|
||||
self._record("contract-456", contract_type="employment", document_type_code="labor_contract"),
|
||||
self._record(
|
||||
"contract-member-owned",
|
||||
tenant_id="tenant-demo-001",
|
||||
generated_by="user-member",
|
||||
generated_by_name="Member",
|
||||
),
|
||||
self._record("contract-other-tenant", tenant_id="tenant-other-001"),
|
||||
]
|
||||
tenant_records = [record for record in records if record["tenant_id"] == current_user["tenant_id"]]
|
||||
if current_user.get("role") != "admin":
|
||||
tenant_records = [
|
||||
record
|
||||
for record in tenant_records
|
||||
if record["metadata"]["generated_by"] == current_user["id"]
|
||||
]
|
||||
if contract_type:
|
||||
tenant_records = [record for record in tenant_records if record["contract_type"] == contract_type]
|
||||
if document_type_code:
|
||||
@@ -546,6 +566,14 @@ class ContractsApiTestCase(unittest.TestCase):
|
||||
"role": "admin",
|
||||
}
|
||||
|
||||
def _login_member(self, tenant_id="tenant-demo-001"):
|
||||
app.dependency_overrides[require_current_user] = lambda: {
|
||||
"id": "user-member",
|
||||
"tenant_id": tenant_id,
|
||||
"display_name": "Member",
|
||||
"role": "member",
|
||||
}
|
||||
|
||||
def test_user_can_list_contract_templates(self):
|
||||
self._login()
|
||||
response = self.client.get("/api/v1/contracts/templates", params={"contract_type": "procurement"})
|
||||
@@ -561,6 +589,14 @@ class ContractsApiTestCase(unittest.TestCase):
|
||||
self.assertEqual(body["data"]["items"][0]["document_type_code"], "labor_contract")
|
||||
self.assertEqual(body["data"]["items"][0]["lifecycle"]["status"], "generated")
|
||||
|
||||
def test_member_generated_contract_list_only_returns_owned_records(self):
|
||||
self._login_member()
|
||||
response = self.client.get("/api/v1/contracts")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = response.json()
|
||||
self.assertEqual(body["data"]["total"], 1)
|
||||
self.assertEqual(body["data"]["items"][0]["id"], "contract-member-owned")
|
||||
|
||||
def test_user_can_update_contract_lifecycle_and_write_audit_log(self):
|
||||
self._login()
|
||||
response = self.client.patch(
|
||||
@@ -1326,6 +1362,48 @@ class ContractGenerationServiceTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEqual(ctx.exception.status_code, 409)
|
||||
|
||||
def test_create_generated_contract_version_rejects_non_owner_member(self):
|
||||
now = datetime.now(timezone.utc)
|
||||
self.db.add(
|
||||
self.GeneratedContract(
|
||||
id="contract-editor-owner-only",
|
||||
tenant_id="tenant-demo-001",
|
||||
template_id="tpl-editor",
|
||||
template_name="Editor Template",
|
||||
contract_type="procurement",
|
||||
document_type_code="procurement_goods",
|
||||
generated_by="user-admin",
|
||||
generated_by_name="Admin",
|
||||
export_format="docx",
|
||||
file_id=None,
|
||||
file_name="editor-owner-only.docx",
|
||||
mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
file_size=10,
|
||||
generated_content="Original contract content.",
|
||||
variable_values_json={},
|
||||
export_options_json={},
|
||||
file_content_base64="",
|
||||
status="generated",
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
self.db.commit()
|
||||
|
||||
with self.assertRaises(HTTPException) as ctx:
|
||||
self.service.create_generated_contract_version(
|
||||
current_user={
|
||||
"id": "user-member",
|
||||
"tenant_id": "tenant-demo-001",
|
||||
"display_name": "Member",
|
||||
"role": "member",
|
||||
},
|
||||
contract_id="contract-editor-owner-only",
|
||||
content="Original contract content.\nAcceptance clause added.",
|
||||
change_summary="member edit attempt",
|
||||
)
|
||||
|
||||
self.assertEqual(ctx.exception.status_code, 404)
|
||||
|
||||
def test_diff_generated_contract_versions_returns_paragraph_changes(self):
|
||||
now = datetime.now(timezone.utc)
|
||||
self.db.add(
|
||||
|
||||
@@ -860,7 +860,31 @@ class _GeneratedContractVersionEditorScreenState
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('编辑合同版本')),
|
||||
appBar: AppBar(
|
||||
title: const Text('编辑合同版本'),
|
||||
actions: [
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: widget.controller.isSaving,
|
||||
builder: (context, isSaving, _) {
|
||||
return Semantics(
|
||||
label: 'save-contract-version-appbar',
|
||||
button: true,
|
||||
child: IconButton(
|
||||
onPressed: isSaving ? null : _save,
|
||||
tooltip: '保存新版本',
|
||||
icon: isSaving
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.save_outlined),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: SafeArea(
|
||||
top: false,
|
||||
minimum: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||
|
||||
@@ -180,6 +180,8 @@ void main() {
|
||||
findsOneWidget);
|
||||
expect(find.bySemanticsLabel('contract-version-content-input'),
|
||||
findsOneWidget);
|
||||
expect(find.bySemanticsLabel('save-contract-version-appbar'),
|
||||
findsOneWidget);
|
||||
|
||||
final summaryField = find.descendant(
|
||||
of: find.bySemanticsLabel('contract-version-change-summary-input'),
|
||||
|
||||
@@ -90,7 +90,15 @@ function Wait-ForAdbDeviceOnline {
|
||||
|
||||
$repairAttempted = $false
|
||||
for ($attempt = 1; $attempt -le $RetryCount; $attempt++) {
|
||||
$state = ((& $script:adb -s $Serial get-state 2>&1) -join [Environment]::NewLine).Trim()
|
||||
$previousErrorActionPreference = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
try {
|
||||
$state = ((& $script:adb -s $Serial get-state 2>&1) -join [Environment]::NewLine).Trim()
|
||||
} catch {
|
||||
$state = ($_.Exception.Message -replace '\s+', ' ').Trim()
|
||||
} finally {
|
||||
$ErrorActionPreference = $previousErrorActionPreference
|
||||
}
|
||||
if ($state -eq "device") {
|
||||
return $true
|
||||
}
|
||||
@@ -140,6 +148,33 @@ function Get-AppSurface {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
function Test-ContractVersionListContainsMarker {
|
||||
param(
|
||||
[string]$UiDump,
|
||||
[string]$Marker
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Marker)) {
|
||||
return $false
|
||||
}
|
||||
if ($UiDump -match 'contract-version-editor') {
|
||||
return $false
|
||||
}
|
||||
if ($UiDump -notmatch 'contract-version-chain') {
|
||||
return $false
|
||||
}
|
||||
|
||||
$normalized = $UiDump.ToLowerInvariant()
|
||||
$parts = $Marker.ToLowerInvariant().Split('_') | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
||||
foreach ($part in $parts) {
|
||||
if ($normalized -notmatch [regex]::Escape($part)) {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
function Get-ApkPath {
|
||||
$candidate = Join-Path $PSScriptRoot "..\build\app\outputs\flutter-apk\app-debug.apk"
|
||||
$resolved = Resolve-Path $candidate -ErrorAction SilentlyContinue
|
||||
@@ -271,6 +306,47 @@ function Get-ContentDescBounds {
|
||||
return $results
|
||||
}
|
||||
|
||||
function Get-NestedButtonBounds {
|
||||
param([string]$UiDump)
|
||||
|
||||
$matches = [regex]::Matches(
|
||||
$UiDump,
|
||||
'class="android\.widget\.Button".*?content-desc="([^"]*)".*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]".*?<node index="\d+" text="" resource-id="" class="android\.widget\.Button".*?content-desc="([^"]*)".*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
|
||||
)
|
||||
$results = @()
|
||||
foreach ($match in $matches) {
|
||||
$results += [pscustomobject]@{
|
||||
OuterContentDesc = $match.Groups[1].Value
|
||||
InnerContentDesc = $match.Groups[6].Value
|
||||
Left = [int]$match.Groups[7].Value
|
||||
Top = [int]$match.Groups[8].Value
|
||||
Right = [int]$match.Groups[9].Value
|
||||
Bottom = [int]$match.Groups[10].Value
|
||||
}
|
||||
}
|
||||
return $results
|
||||
}
|
||||
|
||||
function Get-InnerButtonBounds {
|
||||
param([string]$UiDump)
|
||||
|
||||
$matches = [regex]::Matches(
|
||||
$UiDump,
|
||||
'class="android\.widget\.Button".*?content-desc="([^"]*)".*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"'
|
||||
)
|
||||
$results = @()
|
||||
foreach ($match in $matches) {
|
||||
$results += [pscustomobject]@{
|
||||
ContentDesc = $match.Groups[1].Value
|
||||
Left = [int]$match.Groups[2].Value
|
||||
Top = [int]$match.Groups[3].Value
|
||||
Right = [int]$match.Groups[4].Value
|
||||
Bottom = [int]$match.Groups[5].Value
|
||||
}
|
||||
}
|
||||
return $results
|
||||
}
|
||||
|
||||
function Get-TextBounds {
|
||||
param([string]$UiDump)
|
||||
|
||||
@@ -803,6 +879,24 @@ function Invoke-ContractVersionChainProof {
|
||||
Start-Sleep -Seconds 3
|
||||
$workspaceState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-workspace" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
|
||||
if ($workspaceState.UiDump -match '0 个模板' -or $workspaceState.UiDump -match '0 条历史' -or
|
||||
$workspaceState.UiDump -match '获取合同生成历史失败' -or $workspaceState.UiDump -match '暂无历史结果') {
|
||||
$refreshButton = Get-ContentDescBounds -UiDump $workspaceState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "刷新" } |
|
||||
Select-Object -First 1
|
||||
if (-not $refreshButton) {
|
||||
$refreshButton = Get-InnerButtonBounds -UiDump $workspaceState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "刷新" } |
|
||||
Select-Object -First 1
|
||||
}
|
||||
if ($refreshButton) {
|
||||
$refreshCenter = Get-BoundsCenter -Bounds $refreshButton
|
||||
Invoke-AdbTap -Serial $Serial -X $refreshCenter.X -Y $refreshCenter.Y
|
||||
Start-Sleep -Seconds 4
|
||||
$workspaceState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-workspace-refresh" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
}
|
||||
}
|
||||
|
||||
$historyTab = Get-ContentDescBounds -UiDump $workspaceState.UiDump |
|
||||
Where-Object { $_.ContentDesc -match '^contract-history-tab' } |
|
||||
Select-Object -First 1
|
||||
@@ -958,12 +1052,8 @@ function Invoke-ContractVersionChainProof {
|
||||
Start-Sleep -Milliseconds 400
|
||||
& $script:adb -s $Serial shell input keyevent KEYCODE_MOVE_END | Out-Null
|
||||
Invoke-AdbTextEntry -Serial $Serial -Value " $saveMarker"
|
||||
& $script:adb -s $Serial shell input keyevent KEYCODE_ENTER | Out-Null
|
||||
Start-Sleep -Seconds 6
|
||||
$afterEnterSaveState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-after-enter-save" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
$saveOk = ($afterEnterSaveState.UiDump -match 'contract-version-chain') -and
|
||||
-not ($afterEnterSaveState.UiDump -match 'contract-version-editor')
|
||||
$createdVersionVisible = $afterEnterSaveState.UiDump -match [regex]::Escape($saveMarker)
|
||||
& $script:adb -s $Serial shell input keyevent KEYCODE_BACK | Out-Null
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
|
||||
if (-not $saveOk) {
|
||||
@@ -971,12 +1061,22 @@ function Invoke-ContractVersionChainProof {
|
||||
$saveState = $editorState
|
||||
for ($saveAttempt = 1; $saveAttempt -le 6; $saveAttempt++) {
|
||||
$saveState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-save-button-$saveAttempt" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
$saveButton = Get-ButtonBounds -UiDump $saveState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "保存新版本" } |
|
||||
$saveButton = Get-NestedButtonBounds -UiDump $saveState.UiDump |
|
||||
Where-Object { $_.InnerContentDesc -eq "保存新版本" -or $_.OuterContentDesc -eq "save-contract-version" } |
|
||||
Select-Object -First 1
|
||||
if (-not $saveButton) {
|
||||
$saveButton = Get-ContentDescBounds -UiDump $saveState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "save-contract-version" } |
|
||||
Where-Object { $_.ContentDesc -eq "save-contract-version-appbar" } |
|
||||
Select-Object -First 1
|
||||
}
|
||||
if (-not $saveButton) {
|
||||
$saveButton = Get-InnerButtonBounds -UiDump $saveState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "保存新版本" } |
|
||||
Select-Object -First 1
|
||||
}
|
||||
if (-not $saveButton) {
|
||||
$saveButton = Get-ContentDescBounds -UiDump $saveState.UiDump |
|
||||
Where-Object { $_.ContentDesc -eq "保存新版本" -or $_.ContentDesc -eq "save-contract-version" } |
|
||||
Select-Object -First 1
|
||||
}
|
||||
if (-not $saveButton) {
|
||||
@@ -992,20 +1092,31 @@ function Invoke-ContractVersionChainProof {
|
||||
}
|
||||
|
||||
if ($saveButton -and $editorOpened) {
|
||||
$saveCenter = Get-SafeTapPoint -Bounds $saveButton
|
||||
$saveCenter = if ($saveButton.Top -lt 320) {
|
||||
Get-BoundsCenter -Bounds $saveButton
|
||||
} else {
|
||||
Get-BoundsCenter -Bounds $saveButton
|
||||
}
|
||||
Invoke-AdbTap -Serial $Serial -X $saveCenter.X -Y $saveCenter.Y
|
||||
Start-Sleep -Milliseconds 800
|
||||
if ($saveCenter.Y -gt 1960) {
|
||||
Invoke-AdbTap -Serial $Serial -X $saveCenter.X -Y 1998
|
||||
Start-Sleep -Milliseconds 800
|
||||
}
|
||||
Start-Sleep -Seconds 6
|
||||
|
||||
$afterSaveState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-after-save" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
$saveOk = ($afterSaveState.UiDump -match 'contract-version-chain') -and
|
||||
-not ($afterSaveState.UiDump -match 'contract-version-editor')
|
||||
$createdVersionVisible = $afterSaveState.UiDump -match [regex]::Escape($saveMarker)
|
||||
if (-not $createdVersionVisible) {
|
||||
if ($saveOk) {
|
||||
$createdVersionVisible = Test-ContractVersionListContainsMarker -UiDump $afterSaveState.UiDump -Marker $saveMarker
|
||||
}
|
||||
if ($saveOk -and -not $createdVersionVisible) {
|
||||
for ($createdAttempt = 1; $createdAttempt -le 4; $createdAttempt++) {
|
||||
Invoke-AdbSwipe -Serial $Serial -StartX 540 -StartY 1760 -EndX 540 -EndY 760 -DurationMs 320
|
||||
Start-Sleep -Seconds 1
|
||||
$createdState = Get-SmokeState -Serial $Serial -Label "contract-version-chain-created-version-$createdAttempt" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
if ($createdState.UiDump -match [regex]::Escape($saveMarker)) {
|
||||
if (Test-ContractVersionListContainsMarker -UiDump $createdState.UiDump -Marker $saveMarker) {
|
||||
$createdVersionVisible = $true
|
||||
break
|
||||
}
|
||||
@@ -1446,7 +1557,7 @@ function Try-AutoLogin {
|
||||
Start-Sleep -Milliseconds 800
|
||||
|
||||
$state = Get-SmokeState -Serial $Serial -Label "auto-login-before-submit" -ArtifactDir $ArtifactDir -PackageName $PackageName
|
||||
$buttons = Get-ButtonBounds -UiDump $state.UiDump
|
||||
$buttons = @(Get-ButtonBounds -UiDump $state.UiDump)
|
||||
if ($buttons.Count -gt 0) {
|
||||
$loginButton = $buttons | Sort-Object Bottom, Right | Select-Object -Last 1
|
||||
$buttonCenter = Get-BoundsCenter -Bounds $loginButton
|
||||
@@ -1593,7 +1704,13 @@ if (-not $SkipServerChecks) {
|
||||
$apkPath = Get-ApkPath
|
||||
$apkInstalled = $false
|
||||
if (-not $SkipInstall -and $apkPath) {
|
||||
if (-not (Wait-ForAdbDeviceOnline -Serial $serial -RetryCount 8 -DelaySeconds 2 -RepairOffline)) {
|
||||
throw "Wireless ADB device '$serial' is not online before APK install."
|
||||
}
|
||||
& $adb -s $serial install -r $apkPath | Tee-Object -FilePath (Join-Path $artifactDir "install.txt") | Out-Null
|
||||
if (-not (Wait-ForAdbDeviceOnline -Serial $serial -RetryCount 8 -DelaySeconds 2 -RepairOffline)) {
|
||||
throw "Wireless ADB device '$serial' went offline after APK install."
|
||||
}
|
||||
$apkInstalled = $true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user