chore: publish native ui polish release v2.5.2
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class ConversationInfoActivityTest {
|
||||
@Test
|
||||
public void renderConversationUsesLightweightHeaderMenuAndThreadList() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ConversationInfoActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ConversationInfoActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||
TestConversationInfoActivity activity = Robolectric
|
||||
.buildActivity(TestConversationInfoActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderConversation",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildDetailPayload()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload())
|
||||
);
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(0), "北区试产线回归"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(0), "单线程会话"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(1), "发起群聊"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(1), "选择其他线程加入新群"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(2), "线程详情"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(2), "查看当前线程聊天与项目"));
|
||||
assertTrue(viewTreeContainsText(content, "参与线程"));
|
||||
assertTrue(viewTreeContainsText(content, "硬件审计协作"));
|
||||
assertFalse(viewTreeContainsText(content, "从当前会话选择其他线程,创建新的独立群聊"));
|
||||
assertFalse(viewTreeContainsText(content, "以下线程参与当前会话,点击可查看对应项目详情。"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threadDetailMenuRowStillOpensProjectDetail() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ConversationInfoActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ConversationInfoActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||
TestConversationInfoActivity activity = Robolectric
|
||||
.buildActivity(TestConversationInfoActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderConversation",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildDetailPayload()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload())
|
||||
);
|
||||
|
||||
View threadDetailRow = findClickableViewContainingText(
|
||||
activity.findViewById(R.id.screen_content),
|
||||
"线程详情"
|
||||
);
|
||||
assertNotNull(threadDetailRow);
|
||||
|
||||
threadDetailRow.performClick();
|
||||
|
||||
Intent nextIntent = Shadows.shadowOf(activity).getNextStartedActivity();
|
||||
assertNotNull(nextIntent);
|
||||
assertEquals(
|
||||
ProjectDetailActivity.class.getName(),
|
||||
nextIntent.getComponent().getClassName()
|
||||
);
|
||||
assertEquals(
|
||||
"project-1",
|
||||
nextIntent.getStringExtra(ProjectDetailActivity.EXTRA_PROJECT_ID)
|
||||
);
|
||||
assertEquals(
|
||||
"北区试产线回归",
|
||||
nextIntent.getStringExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
private static JSONObject buildDetailPayload() throws Exception {
|
||||
JSONObject threadMeta = new JSONObject()
|
||||
.put("threadId", "thread-7")
|
||||
.put("folderName", "Boss");
|
||||
JSONObject project = new JSONObject()
|
||||
.put("id", "project-1")
|
||||
.put("name", "北区试产线回归")
|
||||
.put("isGroup", false)
|
||||
.put("deviceIds", new JSONArray().put("mac-studio").put("macbook"))
|
||||
.put("threadMeta", threadMeta);
|
||||
return new JSONObject().put("project", project);
|
||||
}
|
||||
|
||||
private static JSONObject buildParticipantsPayload() throws Exception {
|
||||
JSONArray participants = new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-1")
|
||||
.put("threadDisplayName", "北区试产线回归")
|
||||
.put("folderName", "Boss")
|
||||
.put("deviceId", "Mac Studio")
|
||||
.put("threadId", "thread-7")
|
||||
.put("isSourceProject", true))
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-2")
|
||||
.put("threadDisplayName", "硬件审计协作")
|
||||
.put("folderName", "Boss")
|
||||
.put("deviceId", "Mac Studio")
|
||||
.put("threadId", "thread-8"));
|
||||
return new JSONObject().put("participants", participants);
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (expectedText.contentEquals(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static View findClickableViewContainingText(View root, String expectedText) {
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
if (viewTreeContainsText(root, expectedText) && root.isClickable()) {
|
||||
return root;
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return null;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
View match = findClickableViewContainingText(group.getChildAt(index), expectedText);
|
||||
if (match != null) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class TestConversationInfoActivity extends ConversationInfoActivity {
|
||||
@Override
|
||||
protected void reload() {
|
||||
// Tests render the lightweight info state directly.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class GroupInfoActivityTest {
|
||||
@Test
|
||||
public void renderGroupUsesLightweightHeaderMenuAndMemberList() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(GroupInfoActivity.EXTRA_PROJECT_ID, "group-1")
|
||||
.putExtra(GroupInfoActivity.EXTRA_PROJECT_NAME, "巡检协作群");
|
||||
TestGroupInfoActivity activity = Robolectric
|
||||
.buildActivity(TestGroupInfoActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderGroup",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildDetailPayload()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload())
|
||||
);
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(0), "巡检协作群"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(0), "协作群聊"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(1), "线程详情"));
|
||||
assertTrue(viewTreeContainsText(content.getChildAt(1), "查看当前群聊对应项目"));
|
||||
assertTrue(viewTreeContainsText(content, "群成员"));
|
||||
assertTrue(viewTreeContainsText(content, "Boss 移动控制台"));
|
||||
assertFalse(viewTreeContainsText(content, "群聊成员可点击查看对应项目详情。"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threadDetailMenuRowStillOpensProjectDetail() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(GroupInfoActivity.EXTRA_PROJECT_ID, "group-1")
|
||||
.putExtra(GroupInfoActivity.EXTRA_PROJECT_NAME, "巡检协作群");
|
||||
TestGroupInfoActivity activity = Robolectric
|
||||
.buildActivity(TestGroupInfoActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderGroup",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildDetailPayload()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload())
|
||||
);
|
||||
|
||||
View threadDetailRow = findClickableViewContainingText(
|
||||
activity.findViewById(R.id.screen_content),
|
||||
"线程详情"
|
||||
);
|
||||
assertNotNull(threadDetailRow);
|
||||
|
||||
threadDetailRow.performClick();
|
||||
|
||||
Intent nextIntent = Shadows.shadowOf(activity).getNextStartedActivity();
|
||||
assertNotNull(nextIntent);
|
||||
assertEquals(
|
||||
ProjectDetailActivity.class.getName(),
|
||||
nextIntent.getComponent().getClassName()
|
||||
);
|
||||
assertEquals(
|
||||
"group-1",
|
||||
nextIntent.getStringExtra(ProjectDetailActivity.EXTRA_PROJECT_ID)
|
||||
);
|
||||
assertEquals(
|
||||
"巡检协作群",
|
||||
nextIntent.getStringExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
private static JSONObject buildDetailPayload() throws Exception {
|
||||
JSONObject threadMeta = new JSONObject()
|
||||
.put("threadId", "group-thread-3")
|
||||
.put("folderName", "Boss");
|
||||
JSONObject project = new JSONObject()
|
||||
.put("id", "group-1")
|
||||
.put("name", "巡检协作群")
|
||||
.put("isGroup", true)
|
||||
.put("collaborationMode", "development")
|
||||
.put("threadMeta", threadMeta);
|
||||
return new JSONObject().put("project", project);
|
||||
}
|
||||
|
||||
private static JSONObject buildParticipantsPayload() throws Exception {
|
||||
JSONArray participants = new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "group-1")
|
||||
.put("threadDisplayName", "巡检协作群")
|
||||
.put("folderName", "Boss")
|
||||
.put("deviceId", "Mac Studio")
|
||||
.put("threadId", "group-thread-3")
|
||||
.put("isSourceProject", true))
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-2")
|
||||
.put("threadDisplayName", "Boss 移动控制台")
|
||||
.put("folderName", "Boss")
|
||||
.put("deviceId", "MacBook Pro")
|
||||
.put("threadId", "thread-8"));
|
||||
return new JSONObject().put("participants", participants);
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (expectedText.contentEquals(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static View findClickableViewContainingText(View root, String expectedText) {
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
if (viewTreeContainsText(root, expectedText) && root.isClickable()) {
|
||||
return root;
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return null;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
View match = findClickableViewContainingText(group.getChildAt(index), expectedText);
|
||||
if (match != null) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class TestGroupInfoActivity extends GroupInfoActivity {
|
||||
@Override
|
||||
protected void reload() {
|
||||
// Tests render the lightweight info state directly.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
@@ -110,8 +111,83 @@ public class ProjectDetailActivityUiTest {
|
||||
ReflectionHelpers.ClassParameter.from(boolean.class, true)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(attachmentView, "让 AI 分析"));
|
||||
assertTrue(viewTreeContainsText(attachmentView, "待分析"));
|
||||
assertTrue(viewTreeContainsText(attachmentView, "AI 分析"));
|
||||
assertTrue(viewTreeContainsText(attachmentView, "可分析"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void manualAnalysisAttachmentUsesCompactStatusAndActionCopy() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||
TestProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject attachment = new JSONObject()
|
||||
.put("attachmentId", "att-compact")
|
||||
.put("fileName", "现场录屏.mp4")
|
||||
.put("mimeType", "video/mp4")
|
||||
.put("attachmentKind", "video")
|
||||
.put("analysisState", "ready_manual")
|
||||
.put("fileSizeBytes", 2048);
|
||||
JSONObject message = new JSONObject()
|
||||
.put("id", "msg-compact")
|
||||
.put("kind", "attachment")
|
||||
.put("body", "已发送附件")
|
||||
.put("attachments", new JSONArray().put(attachment));
|
||||
|
||||
View attachmentView = ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"buildAttachmentMessageView",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, message),
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "你"),
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "09:26"),
|
||||
ReflectionHelpers.ClassParameter.from(boolean.class, true)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(attachmentView, "AI 分析"));
|
||||
assertTrue(viewTreeContainsText(attachmentView, "可分析"));
|
||||
assertFalse(viewTreeContainsText(attachmentView, "让 AI 分析"));
|
||||
assertFalse(viewTreeContainsText(attachmentView, "待分析"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completedAnalysisAttachmentPrefersSummaryOverRepeatedState() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线回归");
|
||||
TestProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject attachment = new JSONObject()
|
||||
.put("attachmentId", "att-complete")
|
||||
.put("fileName", "巡检录像.mp4")
|
||||
.put("mimeType", "video/mp4")
|
||||
.put("attachmentKind", "video")
|
||||
.put("analysisState", "completed")
|
||||
.put("analysisSummary", "发现设备旁存在临时工具箱")
|
||||
.put("fileSizeBytes", 4096);
|
||||
JSONObject message = new JSONObject()
|
||||
.put("id", "msg-complete")
|
||||
.put("kind", "attachment")
|
||||
.put("body", "已发送附件")
|
||||
.put("attachments", new JSONArray().put(attachment));
|
||||
|
||||
View attachmentView = ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"buildAttachmentMessageView",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, message),
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "你"),
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "09:26"),
|
||||
ReflectionHelpers.ClassParameter.from(boolean.class, true)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(attachmentView, "发现设备旁存在临时工具箱"));
|
||||
assertFalse(viewTreeContainsText(attachmentView, "已分析"));
|
||||
}
|
||||
|
||||
private static View buildBoundMessageView(TestProjectDetailActivity activity, String messageId, String body) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class ProjectGoalsActivityUiTest {
|
||||
@Test
|
||||
public void renderGoalsUsesCompactWechatCards() throws Exception {
|
||||
TestProjectGoalsActivity activity = Robolectric
|
||||
.buildActivity(TestProjectGoalsActivity.class, new Intent()
|
||||
.putExtra(ProjectGoalsActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectGoalsActivity.EXTRA_PROJECT_NAME, "北区试产线回归"))
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderGoals",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildProject())
|
||||
);
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 已整理项目目标 · 已完成 1/3"));
|
||||
assertTrue(viewTreeContainsSubstring(content, "完成北区试产线全链路回归"));
|
||||
assertTrue(viewTreeContainsSubstring(content, "已完成 · 09:12 由主 Agent 复核"));
|
||||
assertTrue(viewTreeContainsText(content, "当前约束"));
|
||||
assertFalse(viewTreeContainsText(content, "标记完成"));
|
||||
assertFalse(viewTreeContainsText(content, "编辑目标"));
|
||||
assertFalse(((SwipeRefreshLayout) activity.findViewById(R.id.screen_refresh_layout)).isRefreshing());
|
||||
}
|
||||
|
||||
private static JSONObject buildProject() throws Exception {
|
||||
JSONArray goals = new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("id", "goal-1")
|
||||
.put("text", "完成北区试产线全链路回归,覆盖串口、视觉、OTA 和容灾切换。")
|
||||
.put("note", "已完成 · 09:12 由主 Agent 复核")
|
||||
.put("state", "completed"))
|
||||
.put(new JSONObject()
|
||||
.put("id", "goal-2")
|
||||
.put("text", "所有关键步骤必须留下可交接证据,禁止仅口头确认。")
|
||||
.put("note", "进行中 · 允许用户编辑,主 Agent 会同步重排任务")
|
||||
.put("state", "pending"))
|
||||
.put(new JSONObject()
|
||||
.put("id", "goal-3")
|
||||
.put("text", "当线程上下文余量进入 urgent 前,必须完成阶段摘要与 handoff。")
|
||||
.put("note", "待处理 · 主 Agent 会优先把压缩前必须收尾的任务推到前面")
|
||||
.put("state", "pending"));
|
||||
return new JSONObject().put("goals", goals);
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (expectedText.contentEquals(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsSubstring(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (text != null && text.toString().contains(expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsSubstring(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class TestProjectGoalsActivity extends ProjectGoalsActivity {
|
||||
@Override
|
||||
protected void reload() {
|
||||
// Tests drive renderGoals manually.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class ProjectVersionsActivityUiTest {
|
||||
@Test
|
||||
public void renderVersionsUsesReadonlyWechatStyleCards() throws Exception {
|
||||
TestProjectVersionsActivity activity = Robolectric
|
||||
.buildActivity(TestProjectVersionsActivity.class, new Intent()
|
||||
.putExtra(ProjectVersionsActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectVersionsActivity.EXTRA_PROJECT_NAME, "北区试产线回归"))
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderVersions",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildProject())
|
||||
);
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "仅主 Agent 可发布迭代记录"));
|
||||
assertTrue(viewTreeContainsText(content, "v1.2.8 已发布"));
|
||||
assertTrue(viewTreeContainsSubstring(content, "• 优化 OTA 实时提示"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 复核记录"));
|
||||
assertFalse(viewTreeContainsText(content, "版本记录只读"));
|
||||
assertFalse(((SwipeRefreshLayout) activity.findViewById(R.id.screen_refresh_layout)).isRefreshing());
|
||||
}
|
||||
|
||||
private static JSONObject buildProject() throws Exception {
|
||||
JSONArray versions = new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("version", "v1.2.8 已发布")
|
||||
.put("summary", "• 优化 OTA 实时提示\n• 修复主 Agent 版本汇总延迟\n• 补齐北区试产线回归记录")
|
||||
.put("createdAt", "09:40"))
|
||||
.put(new JSONObject()
|
||||
.put("version", "v1.2.7 已归档")
|
||||
.put("summary", "• 接入设备页状态色\n• 新增账号二维码入口\n• 优化会话页排序")
|
||||
.put("createdAt", "昨日"));
|
||||
return new JSONObject().put("versions", versions);
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (expectedText.contentEquals(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsSubstring(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (text != null && text.toString().contains(expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsSubstring(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class TestProjectVersionsActivity extends ProjectVersionsActivity {
|
||||
@Override
|
||||
protected void reload() {
|
||||
// Tests drive renderVersions manually.
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user