Skip to content

Commit 5d4bc6f

Browse files
committed
test: remote and local promo dialog after promo end
1 parent 5244346 commit 5d4bc6f

File tree

3 files changed

+81
-20
lines changed

3 files changed

+81
-20
lines changed

src/services/pro-dialogs.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ define(function (require, exports, module) {
4242
proUpgradeHTML = require("text!./html/pro-upgrade.html"),
4343
proEndedHTML = require("text!./html/promo-ended.html");
4444

45+
// save a copy of window.fetch so that extensions wont tamper with it.
46+
let fetchFn = window.fetch;
47+
4548
function showProUpgradeDialog(trialDays) {
4649
const title = StringUtils.format(Strings.PROMO_UPGRADE_TITLE, proTitle);
4750
const message = StringUtils.format(Strings.PROMO_UPGRADE_MESSAGE, trialDays);
@@ -113,7 +116,7 @@ define(function (require, exports, module) {
113116

114117
try {
115118
const configURL = `${brackets.config.promotions_url}app/config.json`;
116-
const response = await fetch(configURL);
119+
const response = await fetchFn(configURL);
117120
if (!response.ok) {
118121
_showLocalProEndedDialog();
119122
return;
@@ -130,6 +133,14 @@ define(function (require, exports, module) {
130133
}
131134
}
132135

136+
if (Phoenix.isTestWindow) {
137+
window._test_pro_dlg_login_exports = {
138+
setFetchFn: function _setDdateNowFn(fn) {
139+
fetchFn = fn;
140+
}
141+
};
142+
}
143+
133144
exports.showProUpgradeDialog = showProUpgradeDialog;
134145
exports.showProEndedDialog = showProEndedDialog;
135146
});

src/services/promotions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ define(function (require, exports, module) {
331331

332332
// Test-only exports for integration testing
333333
if (Phoenix.isTestWindow) {
334-
window._test_login_exports = {
334+
window._test_promo_login_exports = {
335335
LoginService: LoginService,
336336
ProDialogs: ProDialogs,
337337
_getTrialData: _getTrialData,

test/spec/promotions-integ-test.js

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@ define(function (require, exports, module) {
3030
LoginService,
3131
ProDialogs,
3232
originalAppConfig,
33+
originalFetch,
3334
mockNow = 1000000000000; // Fixed timestamp for consistent testing
3435

3536
beforeAll(async function () {
3637
testWindow = await SpecRunnerUtils.createTestWindowAndRun();
3738

3839
// Access modules from test window
39-
LoginService = testWindow._test_login_exports;
40-
ProDialogs = testWindow._test_login_exports.ProDialogs;
40+
LoginService = testWindow._test_promo_login_exports;
41+
ProDialogs = testWindow._test_promo_login_exports.ProDialogs;
4142

4243
// Debug: Check what's available in the exports
4344
console.log('Debug: Available exports:', Object.keys(LoginService));
@@ -52,6 +53,12 @@ define(function (require, exports, module) {
5253
throw new Error('setDateNowFn not available in test exports');
5354
}
5455

56+
// Set up fetch mocking for pro dialogs
57+
if (testWindow._test_pro_dlg_login_exports && testWindow._test_pro_dlg_login_exports.setFetchFn) {
58+
// Store reference for later restoration
59+
originalFetch = testWindow.fetch;
60+
}
61+
5562
// Store original config and mock AppConfig for tests
5663
originalAppConfig = testWindow.AppConfig;
5764
testWindow.AppConfig = {
@@ -66,9 +73,15 @@ define(function (require, exports, module) {
6673
testWindow.AppConfig = originalAppConfig;
6774
}
6875

76+
// Restore original fetch if it was mocked
77+
if (originalFetch && testWindow._test_pro_dlg_login_exports && testWindow._test_pro_dlg_login_exports.setFetchFn) {
78+
testWindow._test_pro_dlg_login_exports.setFetchFn(originalFetch);
79+
}
80+
6981
testWindow = null;
7082
LoginService = null;
7183
ProDialogs = null;
84+
originalFetch = null;
7285
await SpecRunnerUtils.closeTestWindow();
7386
}, 30000);
7487

@@ -248,7 +261,7 @@ define(function (require, exports, module) {
248261

249262
describe("Trial Expiration", function () {
250263

251-
it("should show promo ended dialog when trial expires (not logged in)", async function () {
264+
async function setupExpiredTrialAndActivate() {
252265
const expiredTrial = {
253266
proVersion: "3.1.0", // Same version as current to trigger ended dialog
254267
endDate: mockNow - LoginService.TRIAL_CONSTANTS.MS_PER_DAY, // Expired yesterday
@@ -269,30 +282,67 @@ define(function (require, exports, module) {
269282
expect(updatedTrialData.proVersion).toBe("3.1.0"); // Should preserve original version
270283
expect(updatedTrialData.endDate).toBe(expiredTrial.endDate); // Should preserve end date
271284

272-
// Wait for modal dialog and verify it's the "ended" dialog
285+
return { expiredTrial, updatedTrialData };
286+
}
287+
288+
it("should show local promo ended dialog when trial expires (offline/fetch fails)", async function () {
289+
// Mock fetch to fail (network error)
290+
if (testWindow._test_pro_dlg_login_exports && testWindow._test_pro_dlg_login_exports.setFetchFn) {
291+
testWindow._test_pro_dlg_login_exports.setFetchFn(() => {
292+
return Promise.reject(new Error('Network error'));
293+
});
294+
}
295+
296+
// Set up expired trial and activate
297+
await setupExpiredTrialAndActivate();
298+
299+
// Wait for modal dialog and verify it's the local "ended" dialog
273300
await testWindow.__PR.waitForModalDialog(".modal");
274301
const modalContent = testWindow.$('.modal');
275302
expect(modalContent.length).toBeGreaterThan(0);
276303

277-
// Check if it's the "ended" dialog (different text than upgrade)
304+
// Verify it's the local dialog (has text content, no iframe)
278305
const dialogText = modalContent.text();
279306
expect(dialogText).toContain('Phoenix Pro');
280307
expect(dialogText).toContain('Trial has ended');
281308

282-
// Close the dialog, so here trial expiration has 2 dialogs, either an offline dialog or online depends
283-
// on config. we just close em blindly. depending on config of
284-
// `${brackets.config.promotions_url}app/config.json`. here we just close both blindly;
285-
try{
286-
//
287-
testWindow.__PR.clickDialogButtonID("secondaryButton");
288-
} catch (e) {
289-
// ignored
290-
}
291-
try{
292-
testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_CANCEL);
293-
} catch (e) {
294-
// ignored
309+
// Verify NO iframe present (local dialog)
310+
const iframes = modalContent.find('iframe');
311+
expect(iframes.length).toBe(0);
312+
313+
// Close local dialog - simpler button structure
314+
testWindow.__PR.clickDialogButtonID("secondaryButton");
315+
await testWindow.__PR.waitForModalDialogClosed(".modal");
316+
});
317+
318+
it("should show remote promo ended dialog when trial expires (online)", async function () {
319+
// Mock fetch to succeed with remote config
320+
if (testWindow._test_pro_dlg_login_exports && testWindow._test_pro_dlg_login_exports.setFetchFn) {
321+
testWindow._test_pro_dlg_login_exports.setFetchFn(() => {
322+
return Promise.resolve({
323+
ok: true,
324+
json: () => Promise.resolve({
325+
upsell_after_trial_url: "https://phcode.io",
326+
upsell_purchase_url: "https://phcode.dev/pricing"
327+
})
328+
});
329+
});
295330
}
331+
332+
// Set up expired trial and activate
333+
await setupExpiredTrialAndActivate();
334+
335+
// Wait for modal dialog and verify it's the remote dialog
336+
await testWindow.__PR.waitForModalDialog(".modal");
337+
const modalContent = testWindow.$('.modal');
338+
expect(modalContent.length).toBeGreaterThan(0);
339+
340+
// Verify it's the remote dialog (contains iframe)
341+
const iframes = modalContent.find('iframe');
342+
expect(iframes.length).toBeGreaterThan(0);
343+
344+
// Close remote dialog - may have complex button structure
345+
testWindow.__PR.clickDialogButtonID(testWindow.__PR.Dialogs.DIALOG_BTN_CANCEL);
296346
await testWindow.__PR.waitForModalDialogClosed(".modal");
297347
});
298348

0 commit comments

Comments
 (0)