@vangbanlanhat/fca-unofficial 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +2 -0
- package/.github/workflows/nodejs.yml +26 -0
- package/.github/workflows/npmpublish.yml +30 -0
- package/.travis.yml +6 -0
- package/CHANGELOG.md +2 -0
- package/DOCS.md +1932 -0
- package/LICENSE-MIT +21 -0
- package/README.md +198 -0
- package/jest.config.js +10 -0
- package/package.json +86 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/actions/addExternalModule.js +34 -0
- package/src/actions/addUserToGroup.js +113 -0
- package/src/actions/changeAdminStatus.js +79 -0
- package/src/actions/changeArchivedStatus.js +55 -0
- package/src/actions/changeBio.js +77 -0
- package/src/actions/changeBlockedStatus.js +47 -0
- package/src/actions/changeGroupImage.js +129 -0
- package/src/actions/changeNickname.js +59 -0
- package/src/actions/changeThreadColor.js +71 -0
- package/src/actions/changeThreadEmoji.js +55 -0
- package/src/actions/createNewGroup.js +86 -0
- package/src/actions/createPoll.js +71 -0
- package/src/actions/deleteMessage.js +56 -0
- package/src/actions/deleteThread.js +56 -0
- package/src/actions/editMessage.js +81 -0
- package/src/actions/forwardAttachment.js +60 -0
- package/src/actions/getAvatarUser.js +77 -0
- package/src/actions/getCurrentUserID.js +11 -0
- package/src/actions/getEmojiUrl.js +37 -0
- package/src/actions/getFriendsList.js +84 -0
- package/src/actions/getMessage.js +833 -0
- package/src/actions/getRegion.js +11 -0
- package/src/actions/getThreadHistory.js +702 -0
- package/src/actions/getThreadHistoryDeprecated.js +93 -0
- package/src/actions/getThreadInfo.js +265 -0
- package/src/actions/getThreadInfoDeprecated.js +80 -0
- package/src/actions/getThreadList.js +274 -0
- package/src/actions/getThreadListDeprecated.js +91 -0
- package/src/actions/getThreadPictures.js +79 -0
- package/src/actions/getUserID.js +66 -0
- package/src/actions/getUserInfo.js +92 -0
- package/src/actions/handleFriendRequest.js +61 -0
- package/src/actions/handleMessageRequest.js +65 -0
- package/src/actions/httpGet.js +70 -0
- package/src/actions/httpPost.js +70 -0
- package/src/actions/index.js +69 -0
- package/src/actions/listenMqtt.js +818 -0
- package/src/actions/logout.js +136 -0
- package/src/actions/markAsDelivered.js +58 -0
- package/src/actions/markAsRead.js +92 -0
- package/src/actions/markAsReadAll.js +50 -0
- package/src/actions/markAsSeen.js +59 -0
- package/src/actions/muteThread.js +52 -0
- package/src/actions/pinMessage.js +86 -0
- package/src/actions/refreshFb_dtsg.js +89 -0
- package/src/actions/removeUserFromGroup.js +79 -0
- package/src/actions/resolvePhotoUrl.js +45 -0
- package/src/actions/searchForThread.js +53 -0
- package/src/actions/searchStickers.js +53 -0
- package/src/actions/sendMessage.js +490 -0
- package/src/actions/sendMessageMqtt.js +235 -0
- package/src/actions/sendTypingIndicator.js +59 -0
- package/src/actions/setMessageReaction.js +140 -0
- package/src/actions/setMessageReactionMqtt.js +72 -0
- package/src/actions/setPostReaction.js +76 -0
- package/src/actions/setTitle.js +86 -0
- package/src/actions/stopListenMqtt.js +63 -0
- package/src/actions/threadColors.js +57 -0
- package/src/actions/unfriend.js +52 -0
- package/src/actions/unsendMessage.js +61 -0
- package/src/index.js +659 -0
- package/src/service/uploadAttachment.js +318 -0
- package/src/utils/base-parts/auth.js +286 -0
- package/src/utils/base-parts/formatters.js +751 -0
- package/src/utils/base-parts/identity.js +169 -0
- package/src/utils/base-parts/network.js +159 -0
- package/src/utils/base-parts/parsing.js +53 -0
- package/src/utils/base-parts/type.js +9 -0
- package/src/utils/base.js +18 -0
- package/src/utils/defaults.js +10 -0
- package/src/utils/format-attachments.js +7 -0
- package/src/utils/format-core.js +8 -0
- package/src/utils/format-events.js +11 -0
- package/src/utils/format-messages.js +10 -0
- package/src/utils/format-presence.js +9 -0
- package/src/utils/format-threads.js +8 -0
- package/src/utils/formatters.js +18 -0
- package/src/utils/identity.js +12 -0
- package/src/utils/index.js +15 -0
- package/src/utils/network.js +12 -0
- package/src/utils/parsing.js +11 -0
- package/src/utils.js +3 -0
- package/test/config.env.example +30 -0
- package/test/data/shareAttach.js +146 -0
- package/test/data/something.mov +0 -0
- package/test/data/test.png +0 -0
- package/test/data/test.txt +7 -0
- package/test/example-appstate.txt +11 -0
- package/test/integration/actions.appstate.integration.test.js +233 -0
- package/test/integration/api.integration.test.js +387 -0
- package/test/integration/env-test-config.js +46 -0
- package/test/integration/login.appstate.integration.test.js +47 -0
- package/test/integration/page.integration.test.js +139 -0
- package/test/jest.setup.js +54 -0
- package/test/unit/actions/actions.basic.test.js +116 -0
- package/test/unit/actions/actions.registry.test.js +36 -0
- package/test/unit/index/index.test.js +109 -0
- package/test/unit/utils/base-parts/auth.test.js +92 -0
- package/test/unit/utils/base-parts/formatters.test.js +53 -0
- package/test/unit/utils/base-parts/identity.test.js +43 -0
- package/test/unit/utils/base-parts/parsing.test.js +38 -0
- package/test/unit/utils/base-parts/type.test.js +23 -0
- package/test/unit/utils/base.test.js +35 -0
- package/vangbanlanhat-fca-unofficial-1.4.2.tgz +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
describe("action behavior basics", function () {
|
|
4
|
+
test("getCurrentUserID returns ctx.userID", function () {
|
|
5
|
+
var factory = require("../../../src/actions/getCurrentUserID");
|
|
6
|
+
var fn = factory({}, {}, { userID: "123456" });
|
|
7
|
+
|
|
8
|
+
expect(fn()).toBe("123456");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("getCurrentUserID supports callback and await style", async function () {
|
|
12
|
+
var factory = require("../../../src/actions/getCurrentUserID");
|
|
13
|
+
var fn = factory({}, {}, { userID: "123456" });
|
|
14
|
+
|
|
15
|
+
var cbValue;
|
|
16
|
+
fn(function (err, value) {
|
|
17
|
+
expect(err).toBeNull();
|
|
18
|
+
cbValue = value;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(cbValue).toBe("123456");
|
|
22
|
+
expect(await fn()).toBe("123456");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("threadColors exposes known palette keys", function () {
|
|
26
|
+
var factory = require("../../../src/actions/threadColors");
|
|
27
|
+
var colors = factory({}, {}, {});
|
|
28
|
+
|
|
29
|
+
expect(colors).toHaveProperty("DefaultBlue");
|
|
30
|
+
expect(colors).toHaveProperty("HotPink");
|
|
31
|
+
expect(colors).toHaveProperty("MessengerBlue");
|
|
32
|
+
expect(typeof colors.DefaultBlue).toBe("string");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("addExternalModule injects dynamic API functions", function () {
|
|
36
|
+
var factory = require("../../../src/actions/addExternalModule");
|
|
37
|
+
var api = {};
|
|
38
|
+
var defaultFuncs = {};
|
|
39
|
+
var ctx = { userID: "42" };
|
|
40
|
+
|
|
41
|
+
var addExternalModule = factory(defaultFuncs, api, ctx);
|
|
42
|
+
|
|
43
|
+
addExternalModule({
|
|
44
|
+
ping: function (_defaultFuncs, _api, _ctx) {
|
|
45
|
+
return function () {
|
|
46
|
+
return _ctx.userID;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(typeof api.ping).toBe("function");
|
|
52
|
+
expect(api.ping()).toBe("42");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("addExternalModule validates input types", function () {
|
|
56
|
+
var factory = require("../../../src/actions/addExternalModule");
|
|
57
|
+
var api = {};
|
|
58
|
+
var addExternalModule = factory({}, api, {});
|
|
59
|
+
|
|
60
|
+
expect(function () {
|
|
61
|
+
addExternalModule("invalid");
|
|
62
|
+
}).toThrow("moduleObj must be an object");
|
|
63
|
+
|
|
64
|
+
expect(function () {
|
|
65
|
+
addExternalModule({ bad: 123 });
|
|
66
|
+
}).toThrow('Item "bad" in moduleObj must be a function');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("addExternalModule supports callback", function () {
|
|
70
|
+
var factory = require("../../../src/actions/addExternalModule");
|
|
71
|
+
var api = {};
|
|
72
|
+
var defaultFuncs = {};
|
|
73
|
+
var ctx = { userID: "42" };
|
|
74
|
+
var addExternalModule = factory(defaultFuncs, api, ctx);
|
|
75
|
+
|
|
76
|
+
var callbackResult;
|
|
77
|
+
var result = addExternalModule({
|
|
78
|
+
ping: function (_defaultFuncs, _api, _ctx) {
|
|
79
|
+
return function () {
|
|
80
|
+
return _ctx.userID;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}, function (err, ok) {
|
|
84
|
+
expect(err).toBeNull();
|
|
85
|
+
callbackResult = ok;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result).toBe(true);
|
|
89
|
+
expect(callbackResult).toBe(true);
|
|
90
|
+
expect(api.ping()).toBe("42");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("getRegion and getEmojiUrl support callback", function () {
|
|
94
|
+
var getRegionFactory = require("../../../src/actions/getRegion");
|
|
95
|
+
var getEmojiUrlFactory = require("../../../src/actions/getEmojiUrl");
|
|
96
|
+
|
|
97
|
+
var getRegion = getRegionFactory({}, {}, { region: "PRN" });
|
|
98
|
+
var getEmojiUrl = getEmojiUrlFactory({}, {}, {});
|
|
99
|
+
|
|
100
|
+
var regionValue;
|
|
101
|
+
getRegion(function (err, value) {
|
|
102
|
+
expect(err).toBeNull();
|
|
103
|
+
regionValue = value;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
var emojiValue;
|
|
107
|
+
getEmojiUrl("😀", 64, function (err, value) {
|
|
108
|
+
expect(err).toBeNull();
|
|
109
|
+
emojiValue = value;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(regionValue).toBe("PRN");
|
|
113
|
+
expect(typeof emojiValue).toBe("string");
|
|
114
|
+
expect(emojiValue).toContain("emoji.php");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var fs = require("fs");
|
|
4
|
+
var path = require("path");
|
|
5
|
+
|
|
6
|
+
var actions = require("../../../src/actions");
|
|
7
|
+
|
|
8
|
+
describe("actions registry", function () {
|
|
9
|
+
test("exports only file-backed action factories", function () {
|
|
10
|
+
var actionsDir = path.join(__dirname, "../../../src/actions");
|
|
11
|
+
var files = fs
|
|
12
|
+
.readdirSync(actionsDir)
|
|
13
|
+
.filter(function (name) {
|
|
14
|
+
return name.endsWith(".js") && name !== "index.js";
|
|
15
|
+
})
|
|
16
|
+
.map(function (name) {
|
|
17
|
+
return name.replace(/\.js$/, "");
|
|
18
|
+
})
|
|
19
|
+
.sort();
|
|
20
|
+
|
|
21
|
+
var exported = Object.keys(actions).sort();
|
|
22
|
+
|
|
23
|
+
exported.forEach(function (name) {
|
|
24
|
+
expect(files.includes(name)).toBe(true);
|
|
25
|
+
expect(typeof actions[name]).toBe("function");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(exported.length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("every exported action is a factory function", function () {
|
|
32
|
+
Object.keys(actions).forEach(function (name) {
|
|
33
|
+
expect(typeof actions[name]).toBe("function");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
describe("src/index", function () {
|
|
4
|
+
test("exports login function and wires actions when using appState", function () {
|
|
5
|
+
return new Promise(function (resolve, reject) {
|
|
6
|
+
jest.isolateModules(function () {
|
|
7
|
+
var actionFactory = jest.fn(function () {
|
|
8
|
+
return function () {
|
|
9
|
+
return "ok";
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
var listenFactory = jest.fn(function () {
|
|
13
|
+
return function () {
|
|
14
|
+
return "listening";
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
jest.doMock("../../../src/actions", function () {
|
|
19
|
+
return {
|
|
20
|
+
foo: actionFactory,
|
|
21
|
+
listenMqtt: listenFactory
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
jest.doMock("npmlog", function () {
|
|
26
|
+
return {
|
|
27
|
+
maxRecordSize: 0,
|
|
28
|
+
level: "info",
|
|
29
|
+
info: jest.fn(),
|
|
30
|
+
warn: jest.fn(),
|
|
31
|
+
error: jest.fn()
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
var jar = {
|
|
36
|
+
setCookie: jest.fn(),
|
|
37
|
+
getCookies: jest.fn(function () {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
cookieString: function () {
|
|
41
|
+
return "c_user=123";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
jest.doMock("../../../src/utils", function () {
|
|
49
|
+
return {
|
|
50
|
+
getType: function (obj) {
|
|
51
|
+
return Object.prototype.toString.call(obj).slice(8, -1);
|
|
52
|
+
},
|
|
53
|
+
setProxy: jest.fn(),
|
|
54
|
+
getJar: function () {
|
|
55
|
+
return jar;
|
|
56
|
+
},
|
|
57
|
+
get: jest.fn(function () {
|
|
58
|
+
return Promise.resolve({ body: "<html></html>" });
|
|
59
|
+
}),
|
|
60
|
+
saveCookies: function () {
|
|
61
|
+
return function (res) {
|
|
62
|
+
return res;
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
makeDefaults: jest.fn(function () {
|
|
66
|
+
return {};
|
|
67
|
+
}),
|
|
68
|
+
getAppState: jest.fn(function () {
|
|
69
|
+
return [];
|
|
70
|
+
})
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
var login = require("../../../src/index");
|
|
75
|
+
expect(typeof login).toBe("function");
|
|
76
|
+
|
|
77
|
+
login(
|
|
78
|
+
{
|
|
79
|
+
appState: [
|
|
80
|
+
{
|
|
81
|
+
key: "c_user",
|
|
82
|
+
value: "123",
|
|
83
|
+
domain: ".facebook.com",
|
|
84
|
+
path: "/",
|
|
85
|
+
expires: Date.now() + 1000
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
email: "",
|
|
89
|
+
password: ""
|
|
90
|
+
},
|
|
91
|
+
{},
|
|
92
|
+
function (err, api) {
|
|
93
|
+
try {
|
|
94
|
+
expect(err).toBeNull();
|
|
95
|
+
expect(typeof api.foo).toBe("function");
|
|
96
|
+
expect(typeof api.listenMqtt).toBe("function");
|
|
97
|
+
expect(api.listen).toBe(api.listenMqtt);
|
|
98
|
+
expect(actionFactory).toHaveBeenCalled();
|
|
99
|
+
expect(listenFactory).toHaveBeenCalled();
|
|
100
|
+
resolve();
|
|
101
|
+
} catch (e) {
|
|
102
|
+
reject(e);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var auth = require("../../../../src/utils/base-parts/auth");
|
|
4
|
+
var network = require("../../../../src/utils/base-parts/network");
|
|
5
|
+
|
|
6
|
+
describe("auth utilities", function () {
|
|
7
|
+
test("formatCookie builds expected cookie string", function () {
|
|
8
|
+
var c = auth.formatCookie(["c_user", "123", "", "/"], "facebook");
|
|
9
|
+
expect(c).toBe("c_user=123; Path=/; Domain=facebook.com");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("makeDefaults merges protocol defaults into get/post/postFormData", async function () {
|
|
13
|
+
var getSpy = jest.spyOn(network, "get").mockResolvedValue({ ok: true });
|
|
14
|
+
var postSpy = jest.spyOn(network, "post").mockResolvedValue({ ok: true });
|
|
15
|
+
var postFormSpy = jest
|
|
16
|
+
.spyOn(network, "postFormData")
|
|
17
|
+
.mockResolvedValue({ ok: true });
|
|
18
|
+
|
|
19
|
+
var html = '<input name="fb_dtsg" value="abc"> revision":123,';
|
|
20
|
+
var ctx = { globalOptions: { userAgent: "ua" } };
|
|
21
|
+
|
|
22
|
+
var defaults = auth.makeDefaults(html, "uid-1", ctx);
|
|
23
|
+
|
|
24
|
+
await defaults.get("https://x", "jar", { q: 1 });
|
|
25
|
+
await defaults.post("https://x", "jar", { p: 1 });
|
|
26
|
+
await defaults.postFormData("https://x", "jar", { f: 1 }, { k: 1 });
|
|
27
|
+
|
|
28
|
+
expect(getSpy).toHaveBeenCalled();
|
|
29
|
+
expect(postSpy).toHaveBeenCalled();
|
|
30
|
+
expect(postFormSpy).toHaveBeenCalled();
|
|
31
|
+
|
|
32
|
+
var getQs = getSpy.mock.calls[0][2];
|
|
33
|
+
var postForm = postSpy.mock.calls[0][2];
|
|
34
|
+
|
|
35
|
+
expect(getQs).toHaveProperty("__user", "uid-1");
|
|
36
|
+
expect(getQs).toHaveProperty("fb_dtsg", "abc");
|
|
37
|
+
expect(postForm).toHaveProperty("jazoest");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("parseAndCheckLogin parses standard JSON response", async function () {
|
|
41
|
+
var ctx = { jar: { setCookie: jest.fn() } };
|
|
42
|
+
var fn = auth.parseAndCheckLogin(ctx, {});
|
|
43
|
+
|
|
44
|
+
var out = await fn({
|
|
45
|
+
statusCode: 200,
|
|
46
|
+
body: '{"ok":1}',
|
|
47
|
+
request: { method: "POST", headers: { "Content-Type": "application/json" } }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(out).toEqual({ ok: 1 });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("parseAndCheckLogin throws for Not logged in marker", async function () {
|
|
54
|
+
var ctx = { jar: { setCookie: jest.fn() } };
|
|
55
|
+
var fn = auth.parseAndCheckLogin(ctx, {});
|
|
56
|
+
|
|
57
|
+
await expect(
|
|
58
|
+
fn({
|
|
59
|
+
statusCode: 200,
|
|
60
|
+
body: '{"error":1357001}',
|
|
61
|
+
request: { method: "POST", headers: { "Content-Type": "application/json" } }
|
|
62
|
+
})
|
|
63
|
+
).rejects.toEqual({ error: "Not logged in." });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("saveCookies writes facebook and messenger cookies", function () {
|
|
67
|
+
var setCookie = jest.fn();
|
|
68
|
+
var jar = { setCookie: setCookie };
|
|
69
|
+
|
|
70
|
+
auth.saveCookies(jar)({
|
|
71
|
+
headers: {
|
|
72
|
+
"set-cookie": ["a=b; domain=.facebook.com; Path=/"]
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(setCookie).toHaveBeenCalled();
|
|
77
|
+
expect(setCookie.mock.calls[0][1]).toBe("https://www.facebook.com");
|
|
78
|
+
expect(setCookie.mock.calls[1][1]).toBe("https://www.messenger.com");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("getAppState combines facebook and messenger cookie stores", function () {
|
|
82
|
+
var jar = {
|
|
83
|
+
getCookies: jest.fn(function (url) {
|
|
84
|
+
if (url === "https://www.facebook.com") return [1];
|
|
85
|
+
if (url === "https://facebook.com") return [2];
|
|
86
|
+
return [3];
|
|
87
|
+
})
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
expect(auth.getAppState(jar)).toEqual([1, 2, 3]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var fmt = require("../../../../src/utils/base-parts/formatters");
|
|
4
|
+
var shareFixture = require("../../../data/shareAttach");
|
|
5
|
+
|
|
6
|
+
describe("formatter utilities", function () {
|
|
7
|
+
test("formatID strips facebook prefixes", function () {
|
|
8
|
+
expect(fmt.formatID("fbid:12345")).toBe("12345");
|
|
9
|
+
expect(fmt.formatID("id.12345")).toBe("12345");
|
|
10
|
+
expect(fmt.formatID("12345")).toBe("12345");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("formatProxyPresence returns null for incomplete payload", function () {
|
|
14
|
+
expect(fmt.formatProxyPresence({ lat: 1 }, "u1")).toBeNull();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("formatProxyPresence and formatPresence normalize shape", function () {
|
|
18
|
+
expect(fmt.formatProxyPresence({ lat: 123, p: { x: 1 } }, "u1")).toEqual({
|
|
19
|
+
type: "presence",
|
|
20
|
+
timestamp: 123000,
|
|
21
|
+
userID: "u1",
|
|
22
|
+
statuses: { x: 1 }
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(fmt.formatPresence({ la: 456, a: { y: 2 } }, "u2")).toEqual({
|
|
26
|
+
type: "presence",
|
|
27
|
+
timestamp: 456000,
|
|
28
|
+
userID: "u2",
|
|
29
|
+
statuses: { y: 2 }
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("formatDate renders GMT date string", function () {
|
|
34
|
+
var d = new Date(Date.UTC(2024, 0, 2, 3, 4, 5));
|
|
35
|
+
expect(fmt.formatDate(d)).toBe("Tue, 02 Jan 2024 03:04:05 GMT");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("formatDeltaMessage parses share fixture", function () {
|
|
39
|
+
var out = fmt.formatDeltaMessage(shareFixture);
|
|
40
|
+
|
|
41
|
+
expect(out).toHaveProperty("type", "message");
|
|
42
|
+
expect(out).toHaveProperty("threadID");
|
|
43
|
+
expect(out).toHaveProperty("messageID");
|
|
44
|
+
expect(Array.isArray(out.attachments)).toBe(true);
|
|
45
|
+
expect(out.attachments.length).toBeGreaterThan(0);
|
|
46
|
+
expect(out.attachments[0]).toHaveProperty("type");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("admin text message type mapping works", function () {
|
|
50
|
+
expect(fmt.getAdminTextMessageType("change_thread_theme")).toBe("log:thread-color");
|
|
51
|
+
expect(fmt.getAdminTextMessageType("other_type")).toBe("other_type");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var identity = require("../../../../src/utils/base-parts/identity");
|
|
4
|
+
|
|
5
|
+
describe("identity utilities", function () {
|
|
6
|
+
test("generateThreadingID includes client id and expected envelope", function () {
|
|
7
|
+
var id = identity.generateThreadingID("clientabc");
|
|
8
|
+
expect(id).toMatch(/^<\d+:\d+-clientabc@mail\.projektitan\.com>$/);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("generateOfflineThreadingID returns decimal numeric string", function () {
|
|
12
|
+
var id = identity.generateOfflineThreadingID();
|
|
13
|
+
expect(id).toMatch(/^\d+$/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("getGUID returns uuid-like id", function () {
|
|
17
|
+
var guid = identity.getGUID();
|
|
18
|
+
expect(guid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("getSignatureID returns hex string", function () {
|
|
22
|
+
expect(identity.getSignatureID()).toMatch(/^[0-9a-f]+$/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("generateTimestampRelative returns hh:mm", function () {
|
|
26
|
+
expect(identity.generateTimestampRelative()).toMatch(/^\d{1,2}:\d{2}$/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("generatePresence returns encoded payload prefix E", function () {
|
|
30
|
+
var p = identity.generatePresence("12345");
|
|
31
|
+
expect(typeof p).toBe("string");
|
|
32
|
+
expect(p.startsWith("E")).toBe(true);
|
|
33
|
+
expect(p.length).toBeGreaterThan(20);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("generateAccessiblityCookie returns URI-encoded JSON", function () {
|
|
37
|
+
var encoded = identity.generateAccessiblityCookie();
|
|
38
|
+
var decoded = JSON.parse(decodeURIComponent(encoded));
|
|
39
|
+
expect(decoded).toHaveProperty("sr");
|
|
40
|
+
expect(decoded).toHaveProperty("sr-ts");
|
|
41
|
+
expect(decoded).toHaveProperty("hcm-ts");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var parsing = require("../../../../src/utils/base-parts/parsing");
|
|
4
|
+
|
|
5
|
+
describe("parsing utilities", function () {
|
|
6
|
+
test("getFrom returns extracted token content", function () {
|
|
7
|
+
var html = "prefix [TOKEN]value123[/TOKEN] suffix";
|
|
8
|
+
expect(parsing.getFrom(html, "[TOKEN]", "[/TOKEN]")).toBe("value123");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("getFrom returns empty when start token missing", function () {
|
|
12
|
+
expect(parsing.getFrom("abc", "<x>", "</x>")).toBe("");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("getFrom throws when end token missing", function () {
|
|
16
|
+
expect(function () {
|
|
17
|
+
parsing.getFrom("<x>abc", "<x>", "</x>");
|
|
18
|
+
}).toThrow("Could not find endTime");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("makeParsable strips for(;;) and merges concatenated objects", function () {
|
|
22
|
+
var raw = "for (;;);{\"a\":1}\r\n {\"b\":2}";
|
|
23
|
+
expect(parsing.makeParsable(raw)).toBe("[{\"a\":1},{\"b\":2}]");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("arrToForm converts list into object", function () {
|
|
27
|
+
var out = parsing.arrToForm([
|
|
28
|
+
{ name: "foo", val: "bar" },
|
|
29
|
+
{ name: "count", val: 3 }
|
|
30
|
+
]);
|
|
31
|
+
expect(out).toEqual({ foo: "bar", count: 3 });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("decodeClientPayload decodes byte array JSON payload", function () {
|
|
35
|
+
var bytes = [123, 34, 97, 34, 58, 49, 125]; // {"a":1}
|
|
36
|
+
expect(parsing.decodeClientPayload(bytes)).toEqual({ a: 1 });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var getType = require("../../../../src/utils/base-parts/type").getType;
|
|
4
|
+
|
|
5
|
+
describe("type.getType", function () {
|
|
6
|
+
test("detects primitives and objects", function () {
|
|
7
|
+
expect(getType(null)).toBe("Null");
|
|
8
|
+
expect(getType(undefined)).toBe("Undefined");
|
|
9
|
+
expect(getType("x")).toBe("String");
|
|
10
|
+
expect(getType(1)).toBe("Number");
|
|
11
|
+
expect(getType({})).toBe("Object");
|
|
12
|
+
expect(getType([])).toBe("Array");
|
|
13
|
+
expect(getType(new Date())).toBe("Date");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("detects function variants", async function () {
|
|
17
|
+
function fn() {}
|
|
18
|
+
async function afn() {}
|
|
19
|
+
|
|
20
|
+
expect(getType(fn)).toBe("Function");
|
|
21
|
+
expect(getType(afn)).toBe("AsyncFunction");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var base = require("../../../src/utils/base");
|
|
4
|
+
|
|
5
|
+
describe("base exports", function () {
|
|
6
|
+
test("exposes core utility API surface", function () {
|
|
7
|
+
var expectedFunctions = [
|
|
8
|
+
"get",
|
|
9
|
+
"post",
|
|
10
|
+
"postFormData",
|
|
11
|
+
"setProxy",
|
|
12
|
+
"getJar",
|
|
13
|
+
"makeDefaults",
|
|
14
|
+
"parseAndCheckLogin",
|
|
15
|
+
"saveCookies",
|
|
16
|
+
"getAppState",
|
|
17
|
+
"getFrom",
|
|
18
|
+
"arrToForm",
|
|
19
|
+
"decodeClientPayload",
|
|
20
|
+
"generateThreadingID",
|
|
21
|
+
"generateOfflineThreadingID",
|
|
22
|
+
"getGUID",
|
|
23
|
+
"getSignatureID",
|
|
24
|
+
"generatePresence",
|
|
25
|
+
"formatMessage",
|
|
26
|
+
"formatDeltaMessage",
|
|
27
|
+
"formatThread",
|
|
28
|
+
"getType"
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
expectedFunctions.forEach(function (name) {
|
|
32
|
+
expect(base).toHaveProperty(name);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
Binary file
|