Skip to content

Commit 8cb1087

Browse files
implementation of responsive DOM
1 parent e39f823 commit 8cb1087

File tree

6 files changed

+274
-8
lines changed

6 files changed

+274
-8
lines changed

src/lib/ctx.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export default (options: Record<string, string>): Context => {
7676
allowedHostnames: config.allowedHostnames || [],
7777
basicAuthorization: basicAuthObj,
7878
smartIgnore: config.smartIgnore ?? false
79+
deferUploads: config.deferUploads ?? false,
7980
},
8081
uploadFilePath: '',
8182
webStaticConfig: [],

src/lib/processSnapshot.ts

Lines changed: 261 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,283 @@ export default class Queue {
1616
private processing: boolean = false;
1717
private processingSnapshot: string = '';
1818
private ctx: Context;
19+
private snapshotNames: Array<string> = [];
20+
private variants: Array<string> = [];
1921

2022
constructor(ctx: Context) {
2123
this.ctx = ctx;
2224
}
2325

24-
enqueue(item: Snapshot): void {
26+
enqueue(item: Snapshot, start: boolean): void {
2527
this.snapshots.push(item);
28+
if(start){
29+
if (!this.processing) {
30+
this.processing = true;
31+
this.processNext();
32+
}
33+
}
34+
}
35+
36+
startProcessingfunc(): void {
2637
if (!this.processing) {
2738
this.processing = true;
2839
this.processNext();
2940
}
3041
}
3142

43+
private generateVariants(snapshot: Snapshot, config: any): void {
44+
// Process web configurations if they exist
45+
46+
if (config.web) {
47+
const browsers = config.web.browsers || [];
48+
const viewports = config.web.viewports || [];
49+
50+
for (const browser of browsers) {
51+
for (const viewport of viewports) {
52+
const width = viewport.width;
53+
const height = viewport.height || 0; // Use 0 if height is not provided
54+
const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
55+
this.variants.push(variant);
56+
}
57+
}
58+
}
59+
60+
// Process mobile configurations if they exist
61+
if (config.mobile) {
62+
const devices = config.mobile.devices || [];
63+
const orientation = config.mobile.orientation || "portrait"; // Default to portrait if not provided
64+
const fullPage = config.mobile.fullPage ?? true; // FullPage defaults to true if not defined
65+
66+
for (const device of devices) {
67+
const variant = `${snapshot.name}_${device}_${orientation}_${fullPage ? 'fullPage' : 'noFullPage'}`;
68+
this.variants.push(variant);
69+
}
70+
}
71+
}
72+
73+
74+
private generateWebVariants(snapshot: Snapshot, webConfig: any): void {
75+
const browsers = webConfig.browsers ?? this.ctx.config.web?.browsers ?? ["chrome", "edge", "firefox", "safari"];
76+
const viewports = webConfig.viewports || [];
77+
78+
for (const browser of browsers) {
79+
for (const viewport of viewports) {
80+
const width = viewport[0];
81+
const height = viewport[1] || 0; // Use 0 if height is not provided
82+
const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
83+
this.variants.push(variant);
84+
}
85+
}
86+
}
87+
88+
private generateMobileVariants(snapshot: Snapshot, mobileConfig: any): void {
89+
const devices = mobileConfig.devices || [];
90+
const orientation = mobileConfig.orientation ?? this.ctx.config.mobile?.orientation ?? "portrait";
91+
const fullPage = mobileConfig.fullPage ?? this.ctx.config.mobile?.fullPage ?? true;
92+
93+
for (const device of devices) {
94+
const variant = `${snapshot.name}_${device}_${orientation}_${fullPage ? 'fullPage' : 'noFullPage'}`;
95+
this.variants.push(variant);
96+
}
97+
}
98+
99+
private filterExistingVariants(snapshot: Snapshot, config: any): boolean {
100+
101+
let drop = true;
102+
103+
if (snapshot.options && snapshot.options.web) {
104+
const webDrop = this.filterWebVariants(snapshot, snapshot.options.web);
105+
if (!webDrop) drop = false;
106+
}
107+
108+
if (snapshot.options && snapshot.options.mobile) {
109+
const mobileDrop = this.filterMobileVariants(snapshot, snapshot.options.mobile);
110+
if (!mobileDrop) drop = false;
111+
}
112+
113+
// Fallback to the global config if neither web nor mobile options are present in snapshot.options
114+
if (!snapshot.options || (snapshot.options && !snapshot.options.web && !snapshot.options.mobile)) {
115+
const configDrop = this.filterVariants(snapshot, config);
116+
if (!configDrop) drop = false;
117+
}
118+
return drop;
119+
}
120+
121+
private filterVariants(snapshot: Snapshot, config: any): boolean {
122+
let allVariantsDropped = true;
123+
124+
// Process web configurations if they exist in config
125+
if (config.web) {
126+
const browsers = config.web.browsers || [];
127+
const viewports = config.web.viewports || [];
128+
129+
for (const browser of browsers) {
130+
for (const viewport of viewports) {
131+
const width = viewport.width;
132+
const height = viewport.height || 0;
133+
const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
134+
135+
if (!this.variants.includes(variant)) {
136+
allVariantsDropped = false; // Found a variant that needs processing
137+
if (!snapshot.options) snapshot.options = {};
138+
if (!snapshot.options.web) snapshot.options.web = { browsers: [], viewports: [] };
139+
140+
if (!snapshot.options.web.browsers.includes(browser)) {
141+
snapshot.options.web.browsers.push(browser);
142+
}
143+
144+
// Check for unique viewports to avoid duplicates
145+
const viewportExists = snapshot.options.web.viewports.some(existingViewport =>
146+
existingViewport[0] === width &&
147+
(existingViewport.length < 2 || existingViewport[1] === height)
148+
);
149+
150+
if (!viewportExists) {
151+
if (height > 0) {
152+
snapshot.options.web.viewports.push([width, height]);
153+
} else {
154+
snapshot.options.web.viewports.push([width]);
155+
}
156+
}
157+
}
158+
}
159+
}
160+
}
161+
162+
// Process mobile configurations if they exist in config
163+
if (config.mobile) {
164+
const devices = config.mobile.devices || [];
165+
const orientation = config.mobile.orientation || "portrait";
166+
const fullPage = config.mobile.fullPage || true;
167+
168+
for (const device of devices) {
169+
const variant = `${snapshot.name}_${device}_${orientation}_${fullPage ? 'fullPage' : 'noFullPage'}`;
170+
171+
if (!this.variants.includes(variant)) {
172+
allVariantsDropped = false; // Found a variant that needs processing
173+
if (!snapshot.options) snapshot.options = {};
174+
if (!snapshot.options.mobile) snapshot.options.mobile = { devices: [], orientation: "portrait", fullPage: true };
175+
176+
if (!snapshot.options.mobile.devices.includes(device)) {
177+
snapshot.options.mobile.devices.push(device);
178+
}
179+
snapshot.options.mobile.orientation = orientation;
180+
snapshot.options.mobile.fullPage = fullPage;
181+
}
182+
}
183+
}
184+
185+
return allVariantsDropped;
186+
}
187+
188+
private filterWebVariants(snapshot: Snapshot, webConfig: any): boolean {
189+
const browsers = webConfig.browsers ?? this.ctx.config.web?.browsers ?? ["chrome", "edge", "firefox", "safari"];
190+
const viewports = webConfig.viewports || [];
191+
let allVariantsDropped = true;
192+
193+
if (!snapshot.options) {
194+
snapshot.options = {};
195+
}
196+
197+
snapshot.options.web = { browsers: [], viewports: [] };
198+
199+
for (const browser of browsers) {
200+
for (const viewport of viewports) {
201+
const width = viewport[0];
202+
const height = viewport[1] || 0;
203+
const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
204+
205+
if (!this.variants.includes(variant)) {
206+
allVariantsDropped = false; // Found a variant that needs processing
207+
if (!snapshot.options.web.browsers.includes(browser)) {
208+
snapshot.options.web.browsers.push(browser);
209+
}
210+
// Only add unique viewports to avoid duplicates
211+
const viewportExists = snapshot.options.web.viewports.some(existingViewport =>
212+
existingViewport[0] === width &&
213+
(existingViewport.length < 2 || existingViewport[1] === height)
214+
);
215+
console.log(variant)
216+
console.log(viewportExists)
217+
if (!viewportExists) {
218+
if (height > 0) {
219+
snapshot.options.web.viewports.push([width, height]);
220+
} else {
221+
snapshot.options.web.viewports.push([width]);
222+
}
223+
}
224+
}
225+
}
226+
}
227+
return allVariantsDropped;
228+
}
229+
230+
231+
private filterMobileVariants(snapshot: Snapshot, mobileConfig: any): boolean {
232+
if (!snapshot.options) {
233+
snapshot.options = {};
234+
}
235+
236+
snapshot.options.mobile = { devices: [], orientation: "portrait", fullPage: true };
237+
238+
const devices = mobileConfig.devices || [];
239+
const orientation = mobileConfig.orientation ?? this.ctx.config.mobile?.orientation ?? "portrait";
240+
const fullPage = mobileConfig.fullPage ?? this.ctx.config.mobile?.fullPage ?? true;
241+
let allVariantsDropped = true;
242+
243+
for (const device of devices) {
244+
const variant = `${snapshot.name}_${device}_${orientation}_${fullPage ? 'fullPage' : 'noFullPage'}`;
245+
246+
if (!this.variants.includes(variant)) {
247+
allVariantsDropped = false; // Found a variant that needs processing
248+
snapshot.options.mobile.devices.push(device);
249+
snapshot.options.mobile.orientation = orientation;
250+
snapshot.options.mobile.fullPage = fullPage;
251+
}
252+
}
253+
return allVariantsDropped;
254+
}
255+
256+
257+
32258
private async processNext(): Promise<void> {
33259
if (!this.isEmpty()) {
34-
const snapshot = this.snapshots.shift();
260+
let snapshot;
261+
if (this.ctx.config.deferUploads){
262+
snapshot = this.snapshots.pop();
263+
} else {
264+
snapshot = this.snapshots.shift();
265+
}
35266
try {
36267
this.processingSnapshot = snapshot?.name;
37-
let { processedSnapshot, warnings } = await processSnapshot(snapshot, this.ctx);
38-
await this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
39-
this.ctx.totalSnapshots++;
40-
this.processedSnapshots.push({ name: snapshot.name, warnings });
268+
let drop = false;
269+
270+
if (snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
271+
drop = this.filterExistingVariants(snapshot, this.ctx.config);
272+
}
273+
274+
if (snapshot && snapshot.name && !this.snapshotNames.includes(snapshot.name)) {
275+
this.snapshotNames.push(snapshot.name);
276+
}
277+
278+
if (snapshot && snapshot.options && snapshot.options.web){
279+
this.generateWebVariants(snapshot, snapshot.options.web);
280+
}
281+
282+
if (snapshot && snapshot.options && snapshot.options.mobile){
283+
this.generateMobileVariants(snapshot, snapshot.options.mobile)
284+
}
285+
286+
if ( (snapshot && !snapshot.options) || (snapshot && snapshot.options && !snapshot.options.web && !snapshot.options.mobile) ) {
287+
this.generateVariants(snapshot, this.ctx.config);
288+
}
289+
290+
if (!drop) {
291+
let { processedSnapshot, warnings } = await processSnapshot(snapshot, this.ctx);
292+
await this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
293+
this.ctx.totalSnapshots++;
294+
this.processedSnapshots.push({ name: snapshot.name, warnings });
295+
}
41296
} catch (error: any) {
42297
this.ctx.log.debug(`snapshot failed; ${error}`);
43298
this.processedSnapshots.push({ name: snapshot.name, error: error.message });

src/lib/schemaValidation.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ const ConfigSchema = {
150150
errorMessage: "Invalid config; password is mandatory"
151151
},
152152
}
153-
}
153+
},
154+
deferUploads: {
155+
type: "boolean",
156+
errorMessage: "Invalid config; deferUploads must be true/false"
157+
},
154158
},
155159
anyOf: [
156160
{ required: ["web"] },

src/lib/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export default async (ctx: Context): Promise<FastifyInstance<Server, IncomingMes
3636
let { snapshot, testType } = request.body;
3737
if (!validateSnapshot(snapshot)) throw new Error(validateSnapshot.errors[0].message);
3838
ctx.testType = testType;
39-
ctx.snapshotQueue?.enqueue(snapshot);
39+
const start = ctx.config.deferUploads ? false : true;
40+
ctx.snapshotQueue?.enqueue(snapshot, start);
4041
replyCode = 200;
4142
replyBody = { data: { message: "success", warnings: [] }};
4243
} catch (error: any) {

src/tasks/processSnapshot.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRen
1010

1111
try {
1212
// wait for snapshot queue to be empty
13+
if(ctx.config.deferUploads){
14+
console.log("started after processing because of deferuploads")
15+
ctx.snapshotQueue?.startProcessingfunc()
16+
}
1317
await new Promise((resolve) => {
1418
let output: string = '';
1519
const intervalId = setInterval(() => {

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface Context {
2525
allowedHostnames: Array<string>;
2626
basicAuthorization: basicAuth | undefined;
2727
smartIgnore: boolean;
28+
deferUploads: boolean;
2829
};
2930
uploadFilePath: string;
3031
webStaticConfig: WebStaticConfig;

0 commit comments

Comments
 (0)