Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/renderer/src/automation_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export enum AutomationErrorType {
facebook_runJob_deleteWallPosts_ClickNextFailed = "facebook_runJob_deleteWallPosts_ClickNextFailed",
facebook_runJob_deleteWallPosts_DialogNotFound = "facebook_runJob_deleteWallPosts_DialogNotFound",
facebook_runJob_deleteWallPosts_SelectDeleteOptionFailed = "facebook_runJob_deleteWallPosts_SelectDeleteOptionFailed",
facebook_runJob_deleteWallPosts_SelectUntagOptionFailed = "facebook_runJob_deleteWallPosts_SelectUntagOptionFailed",
facebook_runJob_deleteWallPosts_SelectHideOptionFailed = "facebook_runJob_deleteWallPosts_SelectHideOptionFailed",
facebook_runJob_deleteWallPosts_ClickDoneFailed = "facebook_runJob_deleteWallPosts_ClickDoneFailed",
facebook_runJob_deleteWallPosts_CompletionTimeout = "facebook_runJob_deleteWallPosts_CompletionTimeout",
}
Expand Down Expand Up @@ -269,6 +271,10 @@ export const AutomationErrorTypeToMessage = {
"Failed to open the Manage posts dialog on Facebook",
[AutomationErrorType.facebook_runJob_deleteWallPosts_SelectDeleteOptionFailed]:
"Failed to select the delete posts option on Facebook",
[AutomationErrorType.facebook_runJob_deleteWallPosts_SelectUntagOptionFailed]:
"Failed to select the untag posts option on Facebook",
[AutomationErrorType.facebook_runJob_deleteWallPosts_SelectHideOptionFailed]:
"Failed to select the hide posts option on Facebook",
[AutomationErrorType.facebook_runJob_deleteWallPosts_ClickDoneFailed]:
"Failed to click Done while deleting Facebook posts",
[AutomationErrorType.facebook_runJob_deleteWallPosts_CompletionTimeout]:
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@
},
"finished": {
"title": "Jobs Completed",
"wallPosts": "wall posts"
"wallPosts": "wall posts removed (deleted, untagged, or hidden)"
},
"premium": {
"readyToDelete": "You're all set! Let's continue to configure what you want to delete.",
Expand Down Expand Up @@ -620,10 +620,10 @@
"savingLanguage": "I'm checking your language settings.",
"settingLanguageToEnglish": "I'm temporarily changing your language to English (US) for automation.",
"restoringLanguage": "I'm restoring your original language setting.",
"deletingWallPosts": "# I'm deleting all posts from your Facebook wall."
"deletingWallPosts": "# I'm removing all posts from your Facebook wall."
},
"progress": {
"wallPostsDeleted": "Deleted {count} wall posts."
"wallPostsDeleted": "Removed {count} wall posts."
}
}
}
Expand Down
142 changes: 141 additions & 1 deletion src/renderer/src/view_models/FacebookViewModel/jobs_delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
mockElectronAPI,
} from "../../test_util";
import * as DeleteJobs from "./jobs_delete";
import { parseActions, getHighestPriority } from "./jobs_delete";

/**
* Creates a mock FacebookJob for testing
Expand Down Expand Up @@ -195,7 +196,7 @@ describe("FacebookViewModel Delete Jobs", () => {

expect(vm.log).toHaveBeenCalledWith(
"runJobDeleteWallPosts",
"No deletable items found, finishing",
"No actionable items found, finishing",
);
});

Expand Down Expand Up @@ -339,5 +340,144 @@ describe("FacebookViewModel Delete Jobs", () => {

expect(vm.waitForPause).toHaveBeenCalled();
});

it("stops batch and uncheck when priority drops from delete to hide", async () => {
// Items: item 0 supports delete+hide, item 1 supports hide only.
// Expected: check item 0 (priority=delete), check item 1 -> combined=hide -> uncheck item 1 and stop.
// Then proceed to delete item 0. On 2nd batch, clickManagePostsButton fails -> exit.
const vm = createMockFacebookViewModel();
const mockWebview = vm.getWebview()!;

let callCount = 0;
vi.mocked(mockWebview.executeJavaScript).mockImplementation(async () => {
callCount++;
// 1. clickManagePostsButton
if (callCount === 1) return true;
// 2. waitForManagePostsDialog
if (callCount === 2) return true;
// 3. getListsAndItems - two items
if (callCount === 3)
return [
{ listIndex: 0, itemIndex: 0 },
{ listIndex: 0, itemIndex: 1 },
];
// 4. toggleCheckbox item 0 (check)
if (callCount === 4) return true;
// 5. getActionDescription after item 0 — supports delete
if (callCount === 5)
return "You can hide or delete the posts selected.";
// 6. toggleCheckbox item 1 (check)
if (callCount === 6) return true;
// 7. getActionDescription after item 0+1 — combined only supports hide
if (callCount === 7) return "You can hide the posts selected.";
// 8. toggleCheckbox item 1 (uncheck)
if (callCount === 8) return true;
// 9. clickNextButton
if (callCount === 9) return true;
// 10. selectDeletePostsOption
if (callCount === 10) return true;
// 11. clickDoneButton
if (callCount === 11) return true;
// 12. waitForManagePostsDialogToDisappear - dialog gone
if (callCount === 12) return false;
// 13. Second batch: clickManagePostsButton fails -> exit
if (callCount === 13) return false;

return false;
});

await DeleteJobs.runJobDeleteWallPosts(vm, 3);

expect(vm.log).toHaveBeenCalledWith(
"runJobDeleteWallPosts",
expect.stringContaining('changes priority from "delete" to "hide"'),
);
expect(vm.progress.wallPostsDeleted).toBe(1);
});

it("performs untag action when highest priority is untag", async () => {
// Item supports untag+hide. Expected: batch action = untag.
// On 2nd batch, clickManagePostsButton fails -> exit.
const vm = createMockFacebookViewModel();
const mockWebview = vm.getWebview()!;

let callCount = 0;
vi.mocked(mockWebview.executeJavaScript).mockImplementation(async () => {
callCount++;
// 1. clickManagePostsButton
if (callCount === 1) return true;
// 2. waitForManagePostsDialog
if (callCount === 2) return true;
// 3. getListsAndItems - one item
if (callCount === 3) return [{ listIndex: 0, itemIndex: 0 }];
// 4. toggleCheckbox item 0 (check)
if (callCount === 4) return true;
// 5. getActionDescription — untag+hide available
if (callCount === 5)
return "You can untag yourself from or hide the posts selected.";
// 6. clickNextButton
if (callCount === 6) return true;
// 7. selectUntagPostsOption
if (callCount === 7) return true;
// 8. clickDoneButton
if (callCount === 8) return true;
// 9. waitForManagePostsDialogToDisappear - dialog gone
if (callCount === 9) return false;
// 10. Second batch: clickManagePostsButton fails -> exit
if (callCount === 10) return false;

return false;
});

await DeleteJobs.runJobDeleteWallPosts(vm, 3);

expect(vm.log).toHaveBeenCalledWith(
"runJobDeleteWallPosts",
'First item sets batch action to "untag", checked 1/10',
);
expect(vm.progress.wallPostsDeleted).toBe(1);
});
});

describe("parseActions", () => {
it("parses delete+hide from combined description", () => {
expect(
parseActions("You can hide or delete the posts selected."),
).toEqual(["delete", "hide"]);
});

it("parses untag+hide", () => {
expect(
parseActions("You can untag yourself from or hide the posts selected."),
).toEqual(["untag", "hide"]);
});

it("parses hide only", () => {
expect(parseActions("You can hide the posts selected.")).toEqual([
"hide",
]);
});

it("returns empty array for unrecognized text", () => {
expect(parseActions("Something completely different.")).toEqual([]);
});
});

describe("getHighestPriority", () => {
it("returns delete when delete is available", () => {
expect(getHighestPriority(["delete", "hide"])).toBe("delete");
});

it("returns untag over hide", () => {
expect(getHighestPriority(["untag", "hide"])).toBe("untag");
});

it("returns hide when only hide available", () => {
expect(getHighestPriority(["hide"])).toBe("hide");
});

it("returns null for empty actions", () => {
expect(getHighestPriority([])).toBeNull();
});
});
});
Loading
Loading