Merge pull request #10636 from BerkeleyTrue/fix/beta-in-production

Fix(challenges): beta challenges in production
This commit is contained in:
Quincy Larson
2016-09-09 17:57:11 -07:00
committed by GitHub
4 changed files with 216 additions and 11 deletions

View File

@ -1,4 +1,5 @@
import { Observable } from 'rx';
import debug from 'debug';
import { challengeSelector } from './selectors';
import types from './types';
import {
@ -7,13 +8,18 @@ import {
updateCurrentChallenge,
initMap
} from './actions';
import { createMapUi } from '../utils';
import {
createMapUi,
filterComingSoonBetaFromEntities
} from '../utils';
import {
delayedRedirect,
createErrorObservable
} from '../../../redux/actions';
import createNameIdMap from '../../../../utils/create-name-id-map';
const isDev = debug.enabled('fcc:*');
const { fetchChallenge, fetchChallenges, replaceChallenge } = types;
export default function fetchChallengesSaga(action$, getState, { services }) {
@ -58,12 +64,16 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
redirect ? delayedRedirect(redirect) : null
);
}
const filteredEntities = filterComingSoonBetaFromEntities(
entities,
isDev
);
return Observable.of(
fetchChallengesCompleted(
createNameIdMap(entities),
createNameIdMap(filteredEntities),
result
),
initMap(createMapUi(entities, result)),
initMap(createMapUi(filteredEntities, result)),
);
})
.catch(createErrorObservable);

View File

@ -78,7 +78,11 @@ export function getNextChallenge(
// skip is used to skip isComingSoon challenges
block.challenges[ index + 1 + skip ]
];
if (!isDev && nextChallenge && nextChallenge.isComingSoon) {
if (
!isDev &&
nextChallenge &&
(nextChallenge.isComingSoon || nextChallenge.isBeta)
) {
// if we find a next challenge and it is a coming soon
// recur with plus one to skip this challenge
return getNextChallenge(current, entities, { isDev, skip: skip + 1 });
@ -248,6 +252,31 @@ export function getMouse(e, [dx, dy]) {
return [pageX - dx, pageY - dy];
}
export function filterCommingSoonBetaChallenge(
isDev = false,
{ isComingSoon, isBeta }
) {
return !(isComingSoon || isBeta) ||
isDev;
}
export function filterComingSoonBetaFromEntities(
{ challenge: challengeMap, ...rest },
isDev = false
) {
const filter = filterCommingSoonBetaChallenge.bind(null, isDev);
return {
...rest,
challenge: Object.keys(challengeMap)
.map(dashedName => challengeMap[dashedName])
.filter(filter)
.reduce((challengeMap, challenge) => {
challengeMap[challenge.dashedName] = challenge;
return challengeMap;
}, {})
};
}
// interface Node {
// isHidden: Boolean,
// children: Void|[ ...Node ],

View File

@ -4,6 +4,8 @@ import {
getNextChallenge,
getFirstChallengeOfNextBlock,
getFirstChallengeOfNextSuperBlock,
filterCommingSoonBetaChallenge,
filterComingSoonBetaFromEntities,
createMapUi,
traverseMapUi,
getNode,
@ -18,7 +20,7 @@ import {
test('common/app/routes/challenges/utils', function(t) {
t.test('getNextChallenge', t => {
t.plan(5);
t.plan(7);
t.test('should return falsey when current challenge is not found', t => {
t.plan(1);
const entities = {
@ -166,6 +168,80 @@ test('common/app/routes/challenges/utils', function(t) {
comingSoon
);
});
t.test('should skip isBeta challenge', t => {
t.plan(1);
const currentChallenge = {
dashedName: 'current-challenge',
block: 'current-block'
};
const beta = {
dashedName: 'beta-challenge',
isBeta: true,
block: 'current-block'
};
const nextChallenge = {
dashedName: 'next-challenge',
block: 'current-block'
};
const shouldBeNext = getNextChallenge(
'current-challenge',
{
challenge: {
'current-challenge': currentChallenge,
'next-challenge': nextChallenge,
'beta-challenge': beta,
'beta-challenge2': beta
},
block: {
'current-block': {
challenges: [
'current-challenge',
'beta-challenge',
'beta-challenge2',
'next-challenge'
]
}
}
}
);
t.isEqual(shouldBeNext, nextChallenge);
});
t.test('should not skip isBeta challenge if in dev', t => {
t.plan(1);
const currentChallenge = {
dashedName: 'current-challenge',
block: 'current-block'
};
const beta = {
dashedName: 'beta-challenge',
isBeta: true,
block: 'current-block'
};
const nextChallenge = {
dashedName: 'next-challenge',
block: 'current-block'
};
const entities = {
challenge: {
'current-challenge': currentChallenge,
'next-challenge': nextChallenge,
'beta-challenge': beta
},
block: {
'current-block': {
challenges: [
'current-challenge',
'beta-challenge',
'next-challenge'
]
}
}
};
t.isEqual(
getNextChallenge('current-challenge', entities, { isDev: true }),
beta
);
});
});
t.test('getFirstChallengeOfNextBlock', t => {
@ -784,6 +860,91 @@ test('common/app/routes/challenges/utils', function(t) {
);
});
});
t.test('filterCommingSoonBetaChallenge', t => {
t.plan(4);
t.test('should return true when not coming-soon/beta', t => {
let isDev;
t.ok(filterCommingSoonBetaChallenge(isDev, {}));
t.ok(filterCommingSoonBetaChallenge(true, {}));
t.end();
});
t.test('should return false when isComingSoon', t => {
let isDev;
t.notOk(filterCommingSoonBetaChallenge(isDev, { isComingSoon: true }));
t.end();
});
t.test('should return false when isBeta', t => {
let isDev;
t.notOk(filterCommingSoonBetaChallenge(isDev, { isBeta: true }));
t.end();
});
t.test('should always return true when in dev', t => {
let isDev = true;
t.ok(filterCommingSoonBetaChallenge(isDev, { isBeta: true }));
t.ok(filterCommingSoonBetaChallenge(isDev, { isComingSoon: true }));
t.ok(filterCommingSoonBetaChallenge(
isDev,
{ isBeta: true, isCompleted: true }
));
t.end();
});
});
t.test('filterComingSoonBetaFromEntities', t => {
t.plan(2);
t.test('should filter isBeta|coming-soon by default', t => {
t.plan(2);
const normalChallenge = { dashedName: 'normal-challenge' };
const entities = {
challenge: {
'coming-soon': {
isComingSoon: true
},
'is-beta': {
isBeta: true
},
[normalChallenge.dashedName]: normalChallenge
}
};
const actual = filterComingSoonBetaFromEntities(entities);
t.isEqual(
Object.keys(actual.challenge).length,
1,
'did not filter the correct amount of challenges'
);
t.isEqual(
actual.challenge[normalChallenge.dashedName],
normalChallenge,
'did not return the correct challenge'
);
});
t.test('should not filter isBeta|coming-soon when isDev', t => {
t.plan(1);
const normalChallenge = { dashedName: 'normal-challenge' };
const entities = {
challenge: {
'coming-soon': {
dashedName: 'coming-soon',
isComingSoon: true
},
'is-beta': {
dashedName: 'is-beta',
isBeta: true
},
'is-both': {
dashedName: 'is-both',
isBeta: true
},
[normalChallenge.dashedName]: normalChallenge
}
};
const actual = filterComingSoonBetaFromEntities(entities, true);
t.isEqual(
Object.keys(actual.challenge).length,
4,
'filtered challenges'
);
});
});
t.test('createMapUi', t => {
t.plan(3);
t.test('should return an `{}` when proper args not supplied', t => {

View File

@ -8,10 +8,15 @@ const isBeta = !!process.env.BETA;
const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint)/i;
const log = debug('fcc:services:map');
function shouldNotFilterComingSoon({ isComingSoon, isBeta: challengeIsBeta }) {
return isDev ||
!isComingSoon ||
(isBeta && challengeIsBeta);
// if challenge is not isComingSoon or isBeta => load
// if challenge is commingSoon we are in beta||dev => load
// if challenge is beta and we are in beta||dev => load
// else hide
function loadComingSoonOrBetaChallenge({
isComingSoon,
isBeta: challengeIsBeta
}) {
return !(isComingSoon || challengeIsBeta) || isDev || isBeta;
}
function getFirstChallenge(challengeMap$) {
@ -42,7 +47,7 @@ function getChallengeAndBlock(
if (
!block ||
!challenge ||
!shouldNotFilterComingSoon(challenge)
!loadComingSoonOrBetaChallenge(challenge)
) {
return getChallengeByDashedName(
challengeDashedName,
@ -80,7 +85,7 @@ function getChallengeByDashedName(dashedName, challengeMap$, lang) {
.map(key => challengeMap[key]);
})
.filter(challenge => {
return shouldNotFilterComingSoon(challenge) &&
return loadComingSoonOrBetaChallenge(challenge) &&
testChallengeName.test(challenge.name);
})
.last({ defaultValue: null })