Skip to content

Conversation

@sherwinski
Copy link
Contributor

@sherwinski sherwinski commented Nov 27, 2025

Description

1 Line Summary

Adds more tests to increase code coverage.

Details

Adds/expands test across the following modules:

  • Bell
  • PromptsManager
  • CustomLinkManager
  • ServiceWorkerManager
  • UpdateManager
  • SessionManager

Before

 Test Files  52 passed (52)
      Tests  395 passed (395)
   Start at  19:08:24
   Duration  24.31s

 % Coverage report from v8

=============================== Coverage summary ===============================
Statements   : 67.52% ( 3817/5653 )
Branches     : 54.86% ( 1619/2951 )
Functions    : 75.44% ( 925/1226 )
Lines        : 67.69% ( 3697/5461 )
================================================================================

After

 Test Files  55 passed (55)
      Tests  411 passed (411)
   Start at  11:22:18
   Duration  32.88s

 % Coverage report from v8

=============================== Coverage summary ===============================
Statements   : 71.23% ( 4027/5653 )
Branches     : 59.26% ( 1749/2951 )
Functions    : 77.07% ( 945/1226 )
Lines        : 71.47% ( 3903/5461 )
================================================================================

Systems Affected

  • WebSDK
  • Backend
  • Dashboard

Validation

Tests

Info

Checklist

  • All the automated tests pass or I explained why that is not possible
  • I have personally tested this on my machine or explained why that is not possible
  • I have included test coverage for these changes or explained why they are not needed

Programming Checklist
Interfaces:

  • Don't use default export
  • New interfaces are in model files

Functions:

  • Don't use default export
  • All function signatures have return types
  • Helpers should not access any data but rather be given the data to operate on.

Typescript:

  • No Typescript warnings
  • Avoid silencing null/undefined warnings with the exclamation point

Other:

  • Iteration: refrain from using elem of array syntax. Prefer forEach or use map
  • Avoid using global OneSignal accessor for context if possible. Instead, we can pass it to function/constructor so that we don't call OneSignal.context

Screenshots

Info

Checklist

  • I have included screenshots/recordings of the intended results or explained why they are not needed

Related Tickets



This change is Reviewable

- disabled config and stylesheet failure return
- hiding containers when subscribed and `unsubscribeEnable: false`
- injecting markup and click flow (optIn then optOut) with text and class checks
- Different cases where `_getActiveState` returns None, OneSignal, or Third-Party sw
- `_haveParamsChanged` branches (no registration, scope mismatch, missing scriptUrl, href changes, same href)
- `_shoulInstallWorker` branches (SW support, OneSignal config, state, params changed, needs update)
- `_workerNeedsUpdate` where comparing versions
- `_sendPushDeviceRecordUpdate` early return vs triggering `sendOnSessionUpdate`
- `_sendOnSessionUpdate` branches: already sent, not first page, not registered, unsubscribed skip, subscribed path schedules upsert and sets flag
};
bell._setCustomColorsIfSpecified();
const background = document.querySelector<HTMLElement>('.background')!;
expect(background.getAttribute('style')).toContain('#111');
Copy link
Collaborator

@fadi-george fadi-george Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add new lines after expects

const nativeSpy = vi
.spyOn(pm, '_internalShowNativePrompt')
.mockResolvedValue(true);
const slidedownSpy = vi
Copy link
Collaborator

@fadi-george fadi-george Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could just make _internalShowSlidedownPrompt public since it gets mangled anyway
and remove the type assertions

const pm = new PromptsManager(OneSignal._context);
const getOptsSpy = vi
.spyOn(
pm as unknown as {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing here

pageViews: 0,
}),
);
const condSpy = vi
Copy link
Collaborator

@fadi-george fadi-george Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_isPageViewConditionMet can be separated out of this class since theres no this usage,


export function isPageViewConditionMet(options?: DelayedPromptOptions): boolean {
  if (!options || typeof options.pageViews === 'undefined') {
    return false;
  }

  if (!options.autoPrompt || !options.enabled) {
    return false;
  }

  const localPageViews = getLocalPageViewCount();
  return localPageViews >= options.pageViews;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it test can do:

import * as prompts from './PromptsManager';
const { PromptsManager } = prompts;
...
    const slidedownSpy = vi
      .spyOn(prompts, 'isPageViewConditionMet')
      .mockReturnValue(false);

TestEnvironment.initialize();
sm = new SessionManager(OneSignal._context);
notifySpy = vi.spyOn(sm, '_notifySWToUpsertSession');
deactSpy = vi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing, could make public

// ensure user present
User._createOrGetInstance();

vi.spyOn(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move function outside of the class _getOneSignalAndSubscriptionIds -> export function getOneSignalAndSubscriptionIds since theres no this usage

Comment on lines +220 to +221
visSpy.mockRestore();
focusSpy.mockRestore();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont really need

OneSignal._context._dynamicResourceLoader,
'_loadSdkStylesheet',
).mockResolvedValue(ResourceLoadState._Failed);
await mgr._initialize();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: new line after


test('_sendOnSessionUpdate flows: already sent, not first page, not registered, skip when unsubscribed, success path sets flag', async () => {
// Already sent
(mgr as unknown as { _onSessionSent: boolean })._onSessionSent = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid type assertions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could do this instead mgr['_onSessionSent'] = true;

(mgr as unknown as { _onSessionSent: boolean })._onSessionSent = true;
await mgr._sendOnSessionUpdate();
// reset
(mgr as unknown as { _onSessionSent: boolean })._onSessionSent = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mgr['_onSessionSent'] = false;


// Registered but not subscribed and enableOnSession not true -> skip
alreadySpy.mockResolvedValue(true);
const pushSpy = vi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could do:

    const pushSub = createPushSub();
    pushSub._notification_types = NotificationType._NoNativePermission;


const pushSpy = vi
      .spyOn(OneSignal._coreDirector, '_getPushSubscriptionModel')
      .mockResolvedValue(pushSub);

expect(upsertSpy).not.toHaveBeenCalled();

// Subscribed path
pushSpy.mockResolvedValue({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushSub._notification_types = NotificationType._Subscribed;
pushSub.id = 'sub';
pushSpy.mockResolvedValue(pushSub);
await mgr._sendOnSessionUpdate();

mgr as unknown as { _workerNeedsUpdate: () => Promise<boolean> },
'_workerNeedsUpdate',
).mockResolvedValue(false);
await expect(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much type assertion usages

Comment on lines +106 to +110
await expect(
(
mgr as unknown as { _shouldInstallWorker: () => Promise<boolean> }
)._shouldInstallWorker(),
).resolves.toBe(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could do this and similar things for the others:

    await expect(mgr['_shouldInstallWorker']()).resolves.toBe(false);

vi.spyOn(mgr, '_getActiveState').mockResolvedValue(
helpers.ServiceWorkerActiveState._OneSignalWorker,
);
vi.spyOn(
Copy link
Collaborator

@fadi-george fadi-george Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could make _haveParamsChanged public since it just returns a boolean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants