-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Expand file tree
/
Copy pathnewsfeed_spec.js
More file actions
260 lines (221 loc) · 8.95 KB
/
Copy pathnewsfeed_spec.js
File metadata and controls
260 lines (221 loc) · 8.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
const fs = require("node:fs");
const { expect } = require("playwright/test");
const helpers = require("../helpers/global-setup");
const runTests = () => {
let page;
describe("Default configuration", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/default.js");
await helpers.getDocument();
page = helpers.getPage();
});
it("should show the newsfeed title", async () => {
await expect(page.locator(".newsfeed .newsfeed-source")).toContainText("Rodrigo Ramirez Blog");
});
it("should show the newsfeed article", async () => {
await expect(page.locator(".newsfeed .newsfeed-title")).toContainText("QPanel");
});
it("should NOT show the newsfeed description", async () => {
await page.locator(".newsfeed").waitFor({ state: "visible" });
await expect(page.locator(".newsfeed .newsfeed-desc")).toHaveCount(0);
});
});
describe("Custom configuration", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/prohibited_words.js");
await helpers.getDocument();
page = helpers.getPage();
});
it("should not show articles with prohibited words", async () => {
await expect(page.locator(".newsfeed .newsfeed-title")).toContainText("Problema VirtualBox");
});
it("should show the newsfeed description", async () => {
const locator = page.locator(".newsfeed .newsfeed-desc");
await expect(locator).toBeVisible();
const text = await locator.textContent();
expect(text).toMatch(/\S/);
});
});
describe("Basic HTML tags", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/basic_html.js");
await helpers.getDocument();
page = helpers.getPage();
});
it("should render allowlisted formatting tags in title and description", async () => {
await expect(page.locator(".newsfeed .newsfeed-desc")).toBeVisible();
const descHtml = await page.locator(".newsfeed .newsfeed-desc").innerHTML();
expect(descHtml).toContain("<em>");
expect(descHtml).toContain("<strong>");
expect(descHtml).toContain("<u>");
const titleHtml = await page.locator(".newsfeed .newsfeed-title").innerHTML();
expect(titleHtml).toContain("<em>");
});
it("should strip disallowed HTML and not execute injected scripts", async () => {
const descHtml = await page.locator(".newsfeed .newsfeed-desc").innerHTML();
expect(descHtml).not.toContain("<script");
expect(descHtml).not.toContain("onerror");
const xss = await page.evaluate(() => window.__newsfeedXss);
expect(xss).toBeUndefined();
});
});
describe("Invalid configuration", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/incorrect_url.js");
await helpers.getDocument();
page = helpers.getPage();
});
it("should show malformed url warning", async () => {
await expect(page.locator(".newsfeed .small")).toContainText("Error in the Newsfeed module. Malformed url.");
});
});
describe("Ignore items", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/ignore_items.js");
await helpers.getDocument();
page = helpers.getPage();
});
it("should show empty items info message", async () => {
await expect(page.locator(".newsfeed .small")).toContainText("No news at the moment.");
});
});
};
describe("Newsfeed module > Notifications", () => {
let page;
afterAll(async () => {
await helpers.stopApplication();
});
/**
* Helper: call notificationReceived on the newsfeed module directly.
* @param {object} p - playwright page
* @param {string} notification - notification name
* @param {object} payload - notification payload
* @returns {Promise<void>} resolves when the notification has been dispatched
*/
const notify = (p, notification, payload = {}) => p.evaluate(
({ n, pl }) => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.notificationReceived(n, pl, nf);
},
{ n: notification, pl: payload }
);
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/newsfeed/notifications.js");
await helpers.getDocument();
page = helpers.getPage();
await expect(page.locator(".newsfeed .newsfeed-title")).toBeVisible();
});
it("ARTICLE_NEXT should show the next article", async () => {
const title1 = await page.locator(".newsfeed .newsfeed-title").textContent();
await notify(page, "ARTICLE_NEXT");
await expect(page.locator(".newsfeed .newsfeed-title")).not.toContainText(title1.trim());
});
it("ARTICLE_PREVIOUS should return to the previous article", async () => {
// Start at article 0, go to article 1, then back
await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.activeItem = 0;
nf.resetDescrOrFullArticleAndTimer();
nf.updateDom(0);
});
await expect(page.locator(".newsfeed .newsfeed-title")).toContainText("QPanel");
const title0 = await page.locator(".newsfeed .newsfeed-title").textContent();
await notify(page, "ARTICLE_NEXT");
await expect(page.locator(".newsfeed .newsfeed-title")).not.toContainText(title0.trim());
await notify(page, "ARTICLE_PREVIOUS");
await expect(page.locator(".newsfeed .newsfeed-title")).toContainText(title0.trim());
});
it("ARTICLE_NEXT should wrap around from the last article to the first", async () => {
// Jump to the last article
await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.activeItem = nf.newsItems.length - 1;
nf.resetDescrOrFullArticleAndTimer();
nf.updateDom(0);
});
await expect(page.locator(".newsfeed .newsfeed-title")).toBeVisible();
const titleLast = await page.locator(".newsfeed .newsfeed-title").textContent();
await notify(page, "ARTICLE_NEXT");
await expect(page.locator(".newsfeed .newsfeed-title")).not.toContainText(titleLast.trim());
// activeItem should now be 0
const activeItem = await page.evaluate(() => MM.getModules().find((m) => m.name === "newsfeed").activeItem);
expect(activeItem).toBe(0);
});
it("ARTICLE_PREVIOUS should wrap around from the first article to the last", async () => {
await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.activeItem = 0;
nf.resetDescrOrFullArticleAndTimer();
});
await notify(page, "ARTICLE_PREVIOUS");
const activeItem = await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
return { activeItem: nf.activeItem, total: nf.newsItems.length };
});
expect(activeItem.activeItem).toBe(activeItem.total - 1);
});
it("ARTICLE_INFO_REQUEST should respond with title, source, date, desc and raw url", async () => {
await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.activeItem = 0;
nf.resetDescrOrFullArticleAndTimer();
});
const info = await page.evaluate(() => new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error("ARTICLE_INFO_RESPONSE timeout")), 3000);
const origSend = MM.sendNotification.bind(MM);
MM.sendNotification = function (n, p, s) {
if (n === "ARTICLE_INFO_RESPONSE") {
clearTimeout(timer);
MM.sendNotification = origSend;
resolve(p);
}
return origSend(n, p, s);
};
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.notificationReceived("ARTICLE_INFO_REQUEST", {}, nf);
}));
expect(info).toHaveProperty("title");
expect(info).toHaveProperty("source");
expect(info).toHaveProperty("date");
expect(info).toHaveProperty("desc");
expect(info).toHaveProperty("url");
expect(info.title).toBe("QPanel 0.13.0");
expect(info.source).toBe("Rodrigo Ramirez Blog");
// URL must be the raw article URL, not a CORS proxy URL
expect(info.url).toMatch(/^https?:\/\//);
expect(info.url).not.toContain("localhost");
});
it("ARTICLE_LESS_DETAILS should reset the full article view", async () => {
// Simulate full article view being active
await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
nf.config.showFullArticle = true;
nf.articleFrameCheckPending = false;
nf.articleUnavailable = false;
});
await notify(page, "ARTICLE_LESS_DETAILS");
const state = await page.evaluate(() => {
const nf = MM.getModules().find((m) => m.name === "newsfeed");
return { showFullArticle: nf.config.showFullArticle };
});
expect(state.showFullArticle).toBe(false);
// Normal newsfeed title should be visible again
await expect(page.locator(".newsfeed .newsfeed-title")).toBeVisible();
});
});
describe("Newsfeed module", () => {
afterAll(async () => {
await helpers.stopApplication();
});
runTests();
});
describe("Newsfeed module located in config directory", () => {
beforeAll(() => {
fs.cpSync(`${global.root_path}/${global.defaultModulesDir}/newsfeed`, `${global.root_path}/config/newsfeed`, { recursive: true });
process.env.MM_MODULES_DIR = "config";
});
afterAll(async () => {
await helpers.stopApplication();
});
runTests();
});