mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
refactor: [UEPR-71] changed user eligibility logic
This commit is contained in:
parent
ff9a54bfdd
commit
f6b5253264
6 changed files with 50 additions and 91 deletions
2
.github/workflows/ci-cd.yml
vendored
2
.github/workflows/ci-cd.yml
vendored
|
@ -70,7 +70,7 @@ jobs:
|
||||||
SCRATCH_ENV: ${{ vars.SCRATCH_ENV }}
|
SCRATCH_ENV: ${{ vars.SCRATCH_ENV }}
|
||||||
SORTING_HAT_HOST: ${{ vars.SORTING_HAT_HOST }}
|
SORTING_HAT_HOST: ${{ vars.SORTING_HAT_HOST }}
|
||||||
USER_GUIDING_ID: ${{ secrets.USER_GUIDING_ID }}
|
USER_GUIDING_ID: ${{ secrets.USER_GUIDING_ID }}
|
||||||
ONBOARDING_TESTING_TOGGLED: ${{ secrets.ONBOARDING_TESTING_TOGGLED }}
|
ONBOARDING_TEST_ACTIVE: ${{ secrets.ONBOARDING_TEST_ACTIVE }}
|
||||||
ONBOARDING_TESTING_STARTING_DATE: ${{ secrets.ONBOARDING_TESTING_STARTING_DATE }}
|
ONBOARDING_TESTING_STARTING_DATE: ${{ secrets.ONBOARDING_TESTING_STARTING_DATE }}
|
||||||
ONBOARDING_TESTING_ENDING_DATE: ${{ secrets.ONBOARDING_TESTING_ENDING_DATE }}
|
ONBOARDING_TESTING_ENDING_DATE: ${{ secrets.ONBOARDING_TESTING_ENDING_DATE }}
|
||||||
|
|
||||||
|
|
|
@ -7,43 +7,36 @@ const isAdmin = permissions => permissions.admin;
|
||||||
|
|
||||||
const isMuted = permissions => !!Object.keys(permissions.mute_status).length;
|
const isMuted = permissions => !!Object.keys(permissions.mute_status).length;
|
||||||
|
|
||||||
const isDateInRange = (date, startingDate, endingDate) => {
|
const isDateInOnboardingRange = date => {
|
||||||
const dateToCompare = Date.parse(date);
|
const dateToCompare = Date.parse(date);
|
||||||
const startDate = Date.parse(startingDate);
|
const startDate = Date.parse(process.env.ONBOARDING_TESTING_STARTING_DATE);
|
||||||
const endDate = Date.parse(endingDate);
|
const endDate = Date.parse(process.env.ONBOARDING_TESTING_ENDING_DATE);
|
||||||
|
|
||||||
if (dateToCompare >= startDate && dateToCompare <= endDate) {
|
return dateToCompare >= startDate && dateToCompare <= endDate;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isRegisteredInRange = user => {
|
const isRegisteredInRange = user => {
|
||||||
const dateOfJoin = user.dateJoined.split('T')[0];
|
const dateOfJoin = user.dateJoined.split('T')[0];
|
||||||
|
|
||||||
return isDateInRange(
|
return isDateInOnboardingRange(dateOfJoin);
|
||||||
dateOfJoin,
|
|
||||||
process.env.ONBOARDING_TESTING_STARTING_DATE,
|
|
||||||
process.env.ONBOARDING_TESTING_ENDING_DATE
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCurrentDayInRange = () => {
|
const isCurrentDayInRange = () => {
|
||||||
const currentDate = new Date().toJSON()
|
const currentDate = new Date().toJSON()
|
||||||
.split('T')[0];
|
.split('T')[0];
|
||||||
|
|
||||||
return isDateInRange(
|
return isDateInOnboardingRange(currentDate);
|
||||||
currentDate,
|
|
||||||
process.env.ONBOARDING_TESTING_STARTING_DATE,
|
|
||||||
process.env.ONBOARDING_TESTING_ENDING_DATE
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOnboardingTestingToggledOn = () =>
|
const isUserEligible = (user, permissions) =>
|
||||||
JSON.parse(process.env.ONBOARDING_TESTING_TOGGLED);
|
Object.keys(user).length !== 0 &&
|
||||||
|
Object.keys(permissions).length !== 0 &&
|
||||||
const isUserEligible = user =>
|
JSON.parse(process.env.ONBOARDING_TEST_ACTIVE) &&
|
||||||
user.id % 2 === 0 && isRegisteredInRange(user) && isCurrentDayInRange();
|
isRegisteredInRange(user) &&
|
||||||
|
isCurrentDayInRange() &&
|
||||||
|
!isAdmin(permissions) &&
|
||||||
|
!isMuted(permissions) &&
|
||||||
|
!isBanned(user);
|
||||||
|
|
||||||
const calculateAgeGroup = (birthYear, birthMonth) => {
|
const calculateAgeGroup = (birthYear, birthMonth) => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
|
@ -66,14 +59,8 @@ const onboardingTestGroup = user =>
|
||||||
ONBOARDING_TESTING_GROUP_A_NAME :
|
ONBOARDING_TESTING_GROUP_A_NAME :
|
||||||
ONBOARDING_TESTING_GROUP_B_NAME);
|
ONBOARDING_TESTING_GROUP_B_NAME);
|
||||||
|
|
||||||
export const onboardingEligibilityCheck = (user, permissions) =>
|
export const shouldDisplayOnboarding = (user, permissions) =>
|
||||||
Object.keys(user).length !== 0 &&
|
user.id % 2 === 0 && isUserEligible(user, permissions);
|
||||||
Object.keys(permissions).length !== 0 &&
|
|
||||||
isOnboardingTestingToggledOn() &&
|
|
||||||
isUserEligible(user) &&
|
|
||||||
!isAdmin(permissions) &&
|
|
||||||
!isMuted(permissions) &&
|
|
||||||
!isBanned(user);
|
|
||||||
|
|
||||||
export const triggerAnalyticsEvent = eventVaribles => {
|
export const triggerAnalyticsEvent = eventVaribles => {
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
@ -83,12 +70,8 @@ export const triggerAnalyticsEvent = eventVaribles => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendUserProperties = user => {
|
export const sendUserProperties = (user, permissions) => {
|
||||||
if (
|
if (!isUserEligible(user, permissions)) {
|
||||||
!isOnboardingTestingToggledOn() ||
|
|
||||||
!isRegisteredInRange(user) ||
|
|
||||||
!isCurrentDayInRange()
|
|
||||||
) {
|
|
||||||
window.dataLayer.push({
|
window.dataLayer.push({
|
||||||
testGroup: null,
|
testGroup: null,
|
||||||
ageGroup: null,
|
ageGroup: null,
|
||||||
|
|
|
@ -43,7 +43,7 @@ require('./preview.scss');
|
||||||
const frameless = require('../../lib/frameless');
|
const frameless = require('../../lib/frameless');
|
||||||
const {useState, useCallback, useEffect} = require('react');
|
const {useState, useCallback, useEffect} = require('react');
|
||||||
const ProjectJourney = require('../../components/journeys/project-journey/project-journey.jsx');
|
const ProjectJourney = require('../../components/journeys/project-journey/project-journey.jsx');
|
||||||
const {triggerAnalyticsEvent, onboardingEligibilityCheck} = require('../../lib/onboarding.js');
|
const {triggerAnalyticsEvent, shouldDisplayOnboarding} = require('../../lib/onboarding.js');
|
||||||
|
|
||||||
// disable enter key submission on formsy input fields; otherwise formsy thinks
|
// disable enter key submission on formsy input fields; otherwise formsy thinks
|
||||||
// we meant to trigger the "See inside" button. Instead, treat these keypresses
|
// we meant to trigger the "See inside" button. Instead, treat these keypresses
|
||||||
|
@ -157,7 +157,7 @@ const PreviewPresentation = ({
|
||||||
setCanViewProjectJourney(
|
setCanViewProjectJourney(
|
||||||
queryString.parse(location.search, {parseBooleans: true}).showJourney &&
|
queryString.parse(location.search, {parseBooleans: true}).showJourney &&
|
||||||
!userOwnsProject &&
|
!userOwnsProject &&
|
||||||
onboardingEligibilityCheck(user, permissions)
|
shouldDisplayOnboarding(user, permissions)
|
||||||
);
|
);
|
||||||
}, [userOwnsProject, user, permissions]);
|
}, [userOwnsProject, user, permissions]);
|
||||||
const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : '';
|
const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : '';
|
||||||
|
|
|
@ -47,7 +47,7 @@ const {useEffect, useState} = require('react');
|
||||||
const EditorJourney = require('../../components/journeys/editor-journey/editor-journey.jsx');
|
const EditorJourney = require('../../components/journeys/editor-journey/editor-journey.jsx');
|
||||||
const {usePrevious} = require('react-use');
|
const {usePrevious} = require('react-use');
|
||||||
const TutorialsHighlight = require('../../components/journeys/tutorials-highlight/tutorials-highlight.jsx');
|
const TutorialsHighlight = require('../../components/journeys/tutorials-highlight/tutorials-highlight.jsx');
|
||||||
const {triggerAnalyticsEvent, sendUserProperties, onboardingEligibilityCheck} = require('../../lib/onboarding.js');
|
const {triggerAnalyticsEvent, sendUserProperties, shouldDisplayOnboarding} = require('../../lib/onboarding.js');
|
||||||
|
|
||||||
const IntlGUIWithProjectHandler = ({...props}) => {
|
const IntlGUIWithProjectHandler = ({...props}) => {
|
||||||
const [showJourney, setShowJourney] = useState(false);
|
const [showJourney, setShowJourney] = useState(false);
|
||||||
|
@ -62,7 +62,7 @@ const IntlGUIWithProjectHandler = ({...props}) => {
|
||||||
props.projectId &&
|
props.projectId &&
|
||||||
props.projectId !== '0' &&
|
props.projectId !== '0' &&
|
||||||
!isTutorialOpen &&
|
!isTutorialOpen &&
|
||||||
onboardingEligibilityCheck(props.user, props.permissions)
|
shouldDisplayOnboarding(props.user, props.permissions)
|
||||||
) {
|
) {
|
||||||
setShowJourney(true);
|
setShowJourney(true);
|
||||||
}
|
}
|
||||||
|
@ -258,8 +258,8 @@ class Preview extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prevProps.user.id && this.props.user.id) {
|
if (!prevProps.user.id && this.props.user.id && this.props.permissions) {
|
||||||
sendUserProperties(this.props.user);
|
sendUserProperties(this.props.user, this.props.permissions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
@ -548,7 +548,7 @@ class Preview extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const showJourney = queryString.parse(location.search, {parseBooleans: true}).showJourney;
|
const showJourney = queryString.parse(location.search, {parseBooleans: true}).showJourney;
|
||||||
if (showJourney && onboardingEligibilityCheck(this.props.user, this.props.permissions)) {
|
if (showJourney && shouldDisplayOnboarding(this.props.user, this.props.permissions)) {
|
||||||
triggerAnalyticsEvent({
|
triggerAnalyticsEvent({
|
||||||
event: 'tutorial-played',
|
event: 'tutorial-played',
|
||||||
playedProject: this.props.projectInfo.title
|
playedProject: this.props.projectInfo.title
|
||||||
|
@ -667,7 +667,7 @@ class Preview extends React.Component {
|
||||||
}
|
}
|
||||||
handleRemix () {
|
handleRemix () {
|
||||||
const showJourney = queryString.parse(location.search, {parseBooleans: true}).showJourney;
|
const showJourney = queryString.parse(location.search, {parseBooleans: true}).showJourney;
|
||||||
if (showJourney && onboardingEligibilityCheck(this.props.user, this.props.permissions)) {
|
if (showJourney && shouldDisplayOnboarding(this.props.user, this.props.permissions)) {
|
||||||
triggerAnalyticsEvent({
|
triggerAnalyticsEvent({
|
||||||
event: 'tutorial-remixed',
|
event: 'tutorial-remixed',
|
||||||
remixedProject: this.props.projectInfo.title
|
remixedProject: this.props.projectInfo.title
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {onboardingEligibilityCheck} from '../../../src/lib/onboarding';
|
import {shouldDisplayOnboarding} from '../../../src/lib/onboarding';
|
||||||
|
|
||||||
describe('unit test lib/onboarding.js', () => {
|
describe('unit test lib/onboarding.js', () => {
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
|
@ -9,7 +9,7 @@ describe('unit test lib/onboarding.js', () => {
|
||||||
let permissions;
|
let permissions;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
process.env.ONBOARDING_TESTING_TOGGLED = true;
|
process.env.ONBOARDING_TEST_ACTIVE = 'true';
|
||||||
process.env.ONBOARDING_TESTING_STARTING_DATE = startDate.toJSON().split('T')[0];
|
process.env.ONBOARDING_TESTING_STARTING_DATE = startDate.toJSON().split('T')[0];
|
||||||
process.env.ONBOARDING_TESTING_ENDING_DATE = endDate.toJSON().split('T')[0];
|
process.env.ONBOARDING_TESTING_ENDING_DATE = endDate.toJSON().split('T')[0];
|
||||||
|
|
||||||
|
@ -17,95 +17,71 @@ describe('unit test lib/onboarding.js', () => {
|
||||||
permissions = {admin: false, mute_status: {}, new_scratcher: true};
|
permissions = {admin: false, mute_status: {}, new_scratcher: true};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#onboardingEligibilityCheck', () => {
|
describe('#shouldDisplayOnboarding', () => {
|
||||||
describe('when user is eligible to view onboarding journeys', () => {
|
describe('when user is eligible to view onboarding journeys', () => {
|
||||||
describe('when there is time frame for A/B testing', () => {
|
describe('when there is time frame for A/B testing', () => {
|
||||||
test('returns true', () => {
|
test('returns true', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeTruthy();
|
expect(shouldDisplayOnboarding(user, permissions)).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is not eligible to view onboarding journeys', () => {
|
describe('when user is not eligible to view onboarding journeys', () => {
|
||||||
describe('when feature flag is toggled off', () => {
|
describe('when feature flag is toggled off', () => {
|
||||||
beforeEach(() => {
|
|
||||||
process.env.ONBOARDING_TESTING_TOGGLED = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
process.env.ONBOARDING_TEST_ACTIVE = 'false';
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is in other testing group', () => {
|
describe('when user is in other testing group', () => {
|
||||||
beforeEach(() => {
|
|
||||||
user.id = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
user.id = 1;
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is registered outside of time frame', () => {
|
describe('when user is registered outside of time frame', () => {
|
||||||
beforeEach(() => {
|
test('returns false', () => {
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
currentDate.setDate(currentDate.getDate() - 1);
|
currentDate.setDate(currentDate.getDate() - 1);
|
||||||
user.dateJoined = currentDate.toJSON();
|
user.dateJoined = currentDate.toJSON();
|
||||||
});
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
|
|
||||||
test('returns false', () => {
|
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is admin', () => {
|
describe('when user is admin', () => {
|
||||||
beforeEach(() => {
|
|
||||||
permissions.admin = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
permissions.admin = true;
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is muted', () => {
|
describe('when user is muted', () => {
|
||||||
beforeEach(() => {
|
|
||||||
permissions.mute_status = {showWarning: true};
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
permissions.mute_status = {showWarning: true};
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is banned', () => {
|
describe('when user is banned', () => {
|
||||||
beforeEach(() => {
|
|
||||||
user.banned = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
user.banned = true;
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is empty', () => {
|
describe('when user is empty', () => {
|
||||||
beforeEach(() => {
|
|
||||||
user = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
user = {};
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when permissions is empty', () => {
|
describe('when permissions is empty', () => {
|
||||||
beforeEach(() => {
|
|
||||||
permissions = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false', () => {
|
test('returns false', () => {
|
||||||
expect(onboardingEligibilityCheck(user, permissions)).toBeFalsy();
|
permissions = {};
|
||||||
|
expect(shouldDisplayOnboarding(user, permissions)).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -287,8 +287,8 @@ module.exports = {
|
||||||
'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null,
|
'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null,
|
||||||
'process.env.USER_GUIDING_ID': `"${process.env.USER_GUIDING_ID || ''}"`,
|
'process.env.USER_GUIDING_ID': `"${process.env.USER_GUIDING_ID || ''}"`,
|
||||||
'process.env.SORTING_HAT_HOST': `"${process.env.SORTING_HAT_HOST || ''}"`,
|
'process.env.SORTING_HAT_HOST': `"${process.env.SORTING_HAT_HOST || ''}"`,
|
||||||
'process.env.ONBOARDING_TESTING_TOGGLED': `"${
|
'process.env.ONBOARDING_TEST_ACTIVE': `"${
|
||||||
process.env.ONBOARDING_TESTING_TOGGLED || true
|
process.env.ONBOARDING_TEST_ACTIVE || true
|
||||||
}"`,
|
}"`,
|
||||||
'process.env.ONBOARDING_TESTING_STARTING_DATE': `"${
|
'process.env.ONBOARDING_TESTING_STARTING_DATE': `"${
|
||||||
process.env.ONBOARDING_TESTING_STARTING_DATE || '2024-01-20'
|
process.env.ONBOARDING_TESTING_STARTING_DATE || '2024-01-20'
|
||||||
|
|
Loading…
Reference in a new issue