Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add time on site #975

Merged
merged 15 commits into from
Feb 13, 2025
Merged

feat: add time on site #975

merged 15 commits into from
Feb 13, 2025

Conversation

rmi22186
Copy link
Member

@rmi22186 rmi22186 commented Feb 5, 2025

Instructions

  1. PR target branch should be against development
  2. PR title name should follow this format: https://github.com/mParticle/mparticle-workflows/blob/main/.github/workflows/pr-title-check.yml
  3. PR branch prefix should follow this format: https://github.com/mParticle/mparticle-workflows/blob/main/.github/workflows/pr-branch-check-name.yml

Summary

This initial PR gets a class for Foreground

Testing Plan

  • Was this tested locally? If not, explain why. - Tested between multiple tabs to ensure the timer works across tabs.
  • Added unit/integration tests as well

Reference Issue (For mParticle employees only. Ignore if you are an outside contributor)

src/foregroundTimeTracker.ts Outdated Show resolved Hide resolved
}

private handleVisibilityChange(): void {
if (document.hidden) {
Copy link
Member Author

Choose a reason for hiding this comment

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

can we abstract the document out? could this be some sort of "visibility handler"?


private startTracking(): void {
if (!document.hidden) {
this.startTime = performance.now();
Copy link
Member Author

Choose a reason for hiding this comment

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

check for existence of it

this.isActive = true;
setInterval(() => {
console.log(this.totalTime);
});
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
});
}, 1000);

@alexs-mparticle alexs-mparticle force-pushed the refactor/SQDSDKS-4805-inst-manager branch 2 times, most recently from 61c2f16 to 32d51dd Compare February 5, 2025 19:45
@rmi22186 rmi22186 force-pushed the feat/SQDSDKS-7054-tos branch from 944f03a to dba55e2 Compare February 6, 2025 20:55
@rmi22186 rmi22186 changed the base branch from refactor/SQDSDKS-4805-inst-manager to development February 6, 2025 20:59
public startTime: number = 0;
public totalTime: number = 0;

constructor(apiKey: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Technically this isn't really the apiKey but a timerKey since it doesn't make a call to the api. I'm not adamant about this though.

Copy link
Member Author

Choose a reason for hiding this comment

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

true and actually i should pass in the workspaceToken which we use for our general storage as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

actually seems like when i try to use workspaceToken that I need to instantiate the foreground timer in the store which is when the workspaceToken i available. so for now i'm going to keep using it as the apikey but call it the timerKey in line. this is a small detail that I think we can discuss once the other PRs are in.

@@ -66,6 +67,7 @@ export type IntegrationDelays = Dictionary<boolean>;
// https://go.mparticle.com/work/SQDSDKS-6949
export interface IMParticleWebSDKInstance extends MParticleWebSDK {
// Private Properties
_timer: ForegroundTimer;
Copy link
Collaborator

Choose a reason for hiding this comment

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

_timer is too generic. I would call this something like _foregroundTimer, _timeOnSiteTimer or something relevant to the use case.

Copy link
Member Author

Choose a reason for hiding this comment

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

good call. this was originally just a filler to get a POC going. i'll update now

@@ -132,6 +132,7 @@ export default function SessionManager(
});

mpInstance._Store.nullifySession();
mpInstance._timer.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seeing that you're adding this here, I would think we should also add it to the Messages.InformationMessages.NoSessionToEnd and Messages.InformationMessages.AbandonEndSession lines

Copy link
Member Author

Choose a reason for hiding this comment

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

good call

jest.restoreAllMocks();
});

// in Jest, document.hidden by default is false
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would put this comment in the beforeEach section

@rmi22186 rmi22186 force-pushed the feat/SQDSDKS-7054-tos branch 2 times, most recently from 4b4edcb to 2b671dd Compare February 7, 2025 15:29
@rmi22186 rmi22186 marked this pull request as ready for review February 7, 2025 15:29
@rmi22186 rmi22186 changed the base branch from development to time-on-site February 7, 2025 15:31
@rmi22186 rmi22186 force-pushed the feat/SQDSDKS-7054-tos branch from 2b671dd to 77175f9 Compare February 7, 2025 15:38
window.addEventListener('storage', this.syncAcrossTabs);
this.startTracking();

// TODO: this is just to ensure when we load it in an app we can see the timer updates in the console
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sustained Engineering: There could be some value in adding our debug logger here so that if debug === true, we can fire this log. I'm fine with it being a ticket for later.

Copy link
Member Author

Choose a reason for hiding this comment

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

i decided to move this ahead, because by calling getForegroundTime we end up calling updateTotalTime and i just don't want it to create unintended consequences.

test/jest/foregroundTimeTracker.spec.ts Outdated Show resolved Hide resolved
src/foregroundTimeTracker.ts Outdated Show resolved Hide resolved
@@ -136,11 +137,11 @@ describe('batch uploader', () => {
fetchMock.restore();
});

it('should reject batches without events', (done) => {
it.only('should reject batches without events', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you still need this?

@@ -155,6 +156,8 @@ describe('batch uploader', () => {
};

const actualBatch = batchValidator.returnBatch(baseEvent);
console.log('actualBatch.events[0]');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you still need this?

@@ -174,12 +177,11 @@ describe('batch uploader', () => {
);

expect(actualBatchResult.events.length).to.equal(1);
console.log('actualBatchResult.events[0]')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you still need this?

});

afterEach(() => {
jest.restoreAllMocks();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you need to repeat this?

Copy link
Member Author

Choose a reason for hiding this comment

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

each describe block should have this, right? i'm being extra cautious.

Copy link
Collaborator

Choose a reason for hiding this comment

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

IIRC describe blocks should inherit from their parents.

Copy link
Member Author

Choose a reason for hiding this comment

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

ah. from testing it looks like you're right

});

describe('constructor', () => {
let tracker: ForegroundTimeTracker;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see you using this in this block.

Suggested change
let tracker: ForegroundTimeTracker;


it('should call startTracking if the document is not hidden', () => {
Object.defineProperty(document, 'hidden', { value: false });
const startTrackingSpy = jest.spyOn(ForegroundTimeTracker.prototype as any, 'startTracking');
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you should avoid using any here.

Copy link
Member Author

Choose a reason for hiding this comment

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

i was trying a few different things, including creating an IForegroundTimeTracker, but was running into issues because there are a lot of private methods in the ForegroundTimeTracker. let's sync in the morning

expect(startTrackingSpy).toHaveBeenCalled();
});

it('should not call startTracking is the document is not hidden', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
it('should not call startTracking is the document is not hidden', () => {
it('should not call startTracking if the document is hidden', () => {

Copy link
Member Author

Choose a reason for hiding this comment

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

great catch! copy and paste error

});

it('should call handleVisibilityChange when visibility changes', () => {
const spy = jest.spyOn(tracker as any, 'handleVisibilityChange');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here. These should not be any.

test/jest/foregroundTimeTracker.spec.ts Show resolved Hide resolved
expect(syncSpy).toHaveBeenCalled();
});

it('should update totalTime when newValue is passed', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: I would rewrite the test string to reference something other than newValue since it isn't obvious as to what this value is for.

});
});

describe('addHandlers', () => {
Copy link
Collaborator

@alexs-mparticle alexs-mparticle Feb 12, 2025

Choose a reason for hiding this comment

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

Nit: We should follow the convention be of . when referring to a class method’s name and # when referring to an instance method’s name.

@rmi22186 rmi22186 force-pushed the feat/SQDSDKS-7054-tos branch from 9f43d29 to f4c35a2 Compare February 12, 2025 21:21
package.json Outdated
@@ -1,6 +1,6 @@
{
"name": "@mparticle/web-sdk",
"version": "2.32.3",
"version": "2.32.4",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this was committed accidentally.

@@ -1,12 +1,12 @@
{
"name": "@mparticle/web-sdk",
"version": "2.32.3",
"version": "2.32.4",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this was committed accidentally.

CHANGELOG.md Outdated
@@ -1,3 +1,10 @@
## [2.32.4](https://github.com/mParticle/mparticle-web-sdk/compare/v2.32.3...v2.32.4) (2025-02-10)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this was committed accidentally.

Copy link
Member Author

Choose a reason for hiding this comment

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

looks like this is because i didn't rebase the feature branch properly. this should be good now

@rmi22186 rmi22186 changed the base branch from time-on-site to master February 12, 2025 22:25
@rmi22186 rmi22186 changed the base branch from master to time-on-site February 12, 2025 22:25
@@ -132,17 +132,20 @@ export default function SessionManager(
});

mpInstance._Store.nullifySession();
mpInstance._Store.timeOnSiteTimer.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mpInstance._Store.timeOnSiteTimer.resetTimer();
mpInstance._Store.timeOnSiteTimer?.resetTimer();

// - the SDK's store is not enabled because mParticle.setOptOut was called
// - the devToken is undefined
// - webviewBridgeEnabled is set to false
mpInstance.Logger.verbose(
Messages.InformationMessages.AbandonEndSession
);
mpInstance._Store.timeOnSiteTimer.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mpInstance._Store.timeOnSiteTimer.resetTimer();
mpInstance._Store.timeOnSiteTimer?.resetTimer();

@@ -155,6 +158,8 @@ export default function SessionManager(
mpInstance.Logger.verbose(
Messages.InformationMessages.NoSessionToEnd
);
mpInstance._Store.timeOnSiteTimer.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mpInstance._Store.timeOnSiteTimer.resetTimer();
mpInstance._Store.timeOnSiteTimer?.resetTimer();

@@ -180,6 +185,8 @@ export default function SessionManager(
mpInstance._Store.nullifySession();
}
}

mpInstance._Store.timeOnSiteTimer.resetTimer();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mpInstance._Store.timeOnSiteTimer.resetTimer();
mpInstance._Store.timeOnSiteTimer?.resetTimer();

Copy link
Collaborator

@alexs-mparticle alexs-mparticle left a comment

Choose a reason for hiding this comment

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

🖖

@rmi22186 rmi22186 merged commit ec40bf3 into time-on-site Feb 13, 2025
32 of 36 checks passed
rmi22186 added a commit that referenced this pull request Feb 13, 2025
github-actions bot pushed a commit that referenced this pull request Feb 13, 2025
# [2.33.0](v2.32.5...v2.33.0) (2025-02-13)

### Features

* Add time on site ([#975](#975)) ([3645120](3645120))
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.

2 participants