feat: add a2-english cert and exam (#62855)

This commit is contained in:
Shaun Hamilton
2025-10-16 22:52:37 +02:00
committed by GitHub
parent 59cab66cf2
commit ed568658c1
24 changed files with 53 additions and 12 deletions

View File

@@ -105,6 +105,7 @@ model user {
emailVerifyTTL DateTime? // Null | Undefined
externalId String
githubProfile String? // Undefined
isA2EnglishCert Boolean? // Undefined
isApisMicroservicesCert Boolean? // Undefined
isBackEndCert Boolean? // Undefined
isBanned Boolean? // Undefined

View File

@@ -28,6 +28,7 @@ export const newUser = (email: string) => ({
id: expect.stringMatching(mongodbIdRe),
is2018DataVisCert: false,
is2018FullStackCert: false,
isA2EnglishCert: false,
isApisMicroservicesCert: false,
isBackEndCert: false,
isBanned: false,

View File

@@ -7,6 +7,7 @@ import { pick, omit } from 'lodash-es';
const nullableFlags = [
'is2018DataVisCert',
'is2018FullStackCert',
'isA2EnglishCert',
'isApisMicroservicesCert',
'isBackEndCert',
'isCheater',

View File

@@ -292,6 +292,7 @@ describe('certificate routes', () => {
}
},
isCertMap: {
isA2EnglishCert: false,
isRespWebDesignCert: true,
isRespWebDesignCertV9: false,
isJavascriptCertV9: false,

View File

@@ -21,6 +21,7 @@ import { SHOW_UPCOMING_CHANGES } from '../../utils/env.js';
import { isKnownCertSlug } from '../helpers/certificate-utils.js';
const {
a2EnglishId,
legacyFrontEndChallengeId,
legacyBackEndChallengeId,
legacyDataVisId,
@@ -160,13 +161,15 @@ function createCertTypeIds(challenges: ReturnType<typeof getChallenges>) {
challenges
),
[certTypes.javascriptV9]: getCertById(javascriptV9Id, challenges),
[certTypes.respWebDesignV9]: getCertById(respWebDesignV9Id, challenges)
[certTypes.respWebDesignV9]: getCertById(respWebDesignV9Id, challenges),
// upcoming
[certTypes.a2English]: getCertById(a2EnglishId, challenges)
};
}
interface CertI {
isA2EnglishCert?: boolean;
isRespWebDesignCert?: boolean;
isJsAlgoDataStructCert?: boolean;
isJsAlgoDataStructCertV8?: boolean;
@@ -192,6 +195,7 @@ interface CertI {
function getUserIsCertMap(user: CertI) {
const {
isA2EnglishCert = false,
isRespWebDesignCert = false,
isJsAlgoDataStructCert = false,
isJsAlgoDataStructCertV8 = false,
@@ -216,6 +220,7 @@ function getUserIsCertMap(user: CertI) {
} = user;
return {
isA2EnglishCert,
isRespWebDesignCert,
isJsAlgoDataStructCert,
isJsAlgoDataStructCertV8,
@@ -383,6 +388,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
completedChallenges: true,
is2018DataVisCert: true,
is2018FullStackCert: true,
isA2EnglishCert: true,
isApisMicroservicesCert: true,
isBackEndCert: true,
isDataVisCert: true,

View File

@@ -274,6 +274,7 @@ const publicUserData = {
githubProfile: testUserData.githubProfile,
is2018DataVisCert: testUserData.is2018DataVisCert,
is2018FullStackCert: testUserData.is2018FullStackCert, // TODO: should this be returned? The client doesn't use it at the moment.
isA2EnglishCert: testUserData.isA2EnglishCert,
isApisMicroservicesCert: testUserData.isApisMicroservicesCert,
isBackEndCert: testUserData.isBackEndCert,
isCheater: testUserData.isCheater,
@@ -326,6 +327,7 @@ const sessionUserData = {
const baseProgressData = {
currentChallengeId: '',
isA2EnglishCert: false,
isRespWebDesignCert: false,
is2018DataVisCert: false,
isFrontEndLibsCert: false,
@@ -998,6 +1000,7 @@ describe('userRoutes', () => {
yearsTopContributor: [],
is2018DataVisCert: false,
is2018FullStackCert: false,
isA2EnglishCert: false,
isApisMicroservicesCert: false,
isBackEndCert: false,
isCheater: false,

View File

@@ -667,6 +667,7 @@ export const userGetRoutes: FastifyPluginCallbackTypebox = (
id: true,
is2018DataVisCert: true,
is2018FullStackCert: true,
isA2EnglishCert: true,
isApisMicroservicesCert: true,
isBackEndCert: true,
isCheater: true,

View File

@@ -60,6 +60,7 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
select: {
isBanned: true,
isCheater: true,
isA2EnglishCert: true,
isFrontEndCert: true,
isBackEndCert: true,
isFullStackCert: true,

View File

@@ -187,6 +187,7 @@ const publicUserData = {
githubProfile: testUserData.githubProfile,
is2018DataVisCert: testUserData.is2018DataVisCert,
is2018FullStackCert: testUserData.is2018FullStackCert, // TODO: should this be returned? The client doesn't use it at the moment.
isA2EnglishCert: testUserData.isA2EnglishCert,
isApisMicroservicesCert: testUserData.isApisMicroservicesCert,
isBackEndCert: testUserData.isBackEndCert,
isCheater: testUserData.isCheater,

View File

@@ -6,6 +6,7 @@ export const genericError = Type.Object({
});
export const isCertMap = Type.Object({
isA2EnglishCert: Type.Boolean(),
isRespWebDesignCert: Type.Boolean(),
isRespWebDesignCertV9: Type.Boolean(),
isJavascriptCertV9: Type.Boolean(),

View File

@@ -68,6 +68,7 @@ export const getSessionUser = {
id: Type.String(),
is2018DataVisCert: Type.Boolean(),
is2018FullStackCert: Type.Boolean(),
isA2EnglishCert: Type.Boolean(),
isApisMicroservicesCert: Type.Boolean(),
isBackEndCert: Type.Boolean(),
isCheater: Type.Boolean(),

View File

@@ -52,6 +52,7 @@ export const getPublicProfile = {
githubProfile: Type.Optional(Type.String()),
is2018DataVisCert: Type.Boolean(),
is2018FullStackCert: Type.Boolean(),
isA2EnglishCert: Type.Boolean(),
isApisMicroservicesCert: Type.Boolean(),
isBackEndCert: Type.Boolean(),
isCheater: Type.Boolean(),

View File

@@ -16,6 +16,7 @@ export const createResetProperties = () => ({
currentChallengeId: '',
is2018DataVisCert: false,
is2018FullStackCert: false,
isA2EnglishCert: false,
isApisMicroservicesCert: false,
isBackEndCert: false,
isCollegeAlgebraPyCertV8: false,

View File

@@ -903,7 +903,7 @@ const allStandardCerts = [
certSlug: Certification.A2English,
projects: [
{
id: '651dd3e06ffb500e3f2ce478',
id: '6721db5d9f0c116e6a0fe25a',
title: 'A2 English for Developers Certification Exam',
link: `${a2EnglishBase}/a2-english-for-developers-certification-exam/a2-english-for-developers-certification-exam`,
certSlug: Certification.A2English

View File

@@ -122,6 +122,7 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
completedChallenges,
email,
is2018DataVisCert,
isA2EnglishCert,
isApisMicroservicesCert,
isJavascriptCertV9,
isJsAlgoDataStructCert,
@@ -190,6 +191,7 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
completedChallenges={completedChallenges}
createFlashMessage={createFlashMessage}
is2018DataVisCert={is2018DataVisCert}
isA2EnglishCert={isA2EnglishCert}
isApisMicroservicesCert={isApisMicroservicesCert}
isBackEndCert={isBackEndCert}
isDataAnalysisPyCertV7={isDataAnalysisPyCertV7}

View File

@@ -3,6 +3,7 @@ import { User } from '../../../../redux/prop-types';
export const getCertifications = (user: User) => {
const {
isA2EnglishCert,
isRespWebDesignCert,
isRespWebDesignCertV9,
is2018DataVisCert,
@@ -28,6 +29,7 @@ export const getCertifications = (user: User) => {
return {
hasModernCert:
isA2EnglishCert ||
isRespWebDesignCert ||
isRespWebDesignCertV9 ||
is2018DataVisCert ||
@@ -52,6 +54,11 @@ export const getCertifications = (user: User) => {
isInfosecQaCert,
isFullStackCert,
currentCerts: [
{
show: isA2EnglishCert,
title: 'A2 English for Developers Certification',
certSlug: Certification.A2English
},
{
show: isRespWebDesignCert,
title: 'Responsive Web Design Certification',

View File

@@ -54,6 +54,7 @@ const userProps = {
yearsTopContributor: [],
isDonating: false,
is2018DataVisCert: true,
isA2EnglishCert: true,
isApisMicroservicesCert: true,
isBackEndCert: true,
isDataVisCert: true,

View File

@@ -262,6 +262,7 @@ const defaultTestProps = {
],
createFlashMessage: createFlashMessage,
is2018DataVisCert: false,
isA2EnglishCert: false,
isApisMicroservicesCert: false,
isBackEndCert: false,
isDataVisCert: true,

View File

@@ -47,6 +47,7 @@ const mapDispatchToProps = {
const createCertifiedMap = ({
is2018DataVisCert,
isA2EnglishCert,
isApisMicroservicesCert,
isJavascriptCertV9,
isJsAlgoDataStructCert,
@@ -97,7 +98,7 @@ const createCertifiedMap = ({
[Certification.PythonV9]: false,
[Certification.RelationalDbV9]: false,
[Certification.BackEndDevApisV9]: false,
[Certification.A2English]: false,
[Certification.A2English]: isA2EnglishCert,
[Certification.B1English]: false,
[Certification.A2Spanish]: false,
[Certification.A2Chinese]: false,

View File

@@ -432,6 +432,7 @@ export type ProfileUI = {
export type ClaimedCertifications = {
is2018DataVisCert: boolean;
isA2EnglishCert: boolean;
isApisMicroservicesCert: boolean;
isBackEndCert: boolean;
isDataVisCert: boolean;

View File

@@ -22,7 +22,8 @@ const idToPath = new Map(
'68e008aa5f80c6099d47b3a2': Certification.FrontEndDevLibsV9,
'68e6bd5020effa1586e79855': Certification.PythonV9,
'68e6bd5120effa1586e79856': Certification.RelationalDbV9,
'68e6bd5120effa1586e79857': Certification.BackEndDevApisV9
'68e6bd5120effa1586e79857': Certification.BackEndDevApisV9,
'651dd7e01d697d0aab7833b7': Certification.A2English
})
);

View File

@@ -1,4 +1,3 @@
---
id: 651dd7e01d697d0aab7833b7
title: A2 English for Developers Certification
certification: a2-english-for-developers

View File

@@ -119,7 +119,8 @@ export const certTypes = {
collegeAlgebraPyV8: 'isCollegeAlgebraPyCertV8',
foundationalCSharpV8: 'isFoundationalCSharpCertV8',
jsAlgoDataStructV8: 'isJsAlgoDataStructCertV8',
javascriptV9: 'isJavascriptCertV9'
javascriptV9: 'isJavascriptCertV9',
a2English: 'isA2EnglishCert'
} as const;
export const certIds = {
@@ -147,7 +148,8 @@ export const certIds = {
frontEndLibsV9Id: '68e008aa5f80c6099d47b3a2',
pythonV9Id: '68e6bd5020effa1586e79855',
relationalDbV9Id: '68e6bd5120effa1586e79856',
backEndDevApisV9Id: '68e6bd5120effa1586e79857'
backEndDevApisV9Id: '68e6bd5120effa1586e79857',
a2EnglishId: '651dd7e01d697d0aab7833b7'
};
export const completionHours = {
@@ -171,7 +173,8 @@ export const completionHours = {
[certTypes.collegeAlgebraPyV8]: 300,
[certTypes.foundationalCSharpV8]: 300,
[certTypes.jsAlgoDataStructV8]: 300,
[certTypes.javascriptV9]: 300
[certTypes.javascriptV9]: 300,
[certTypes.a2English]: 300
};
export const certSlugTypeMap = {
@@ -200,7 +203,8 @@ export const certSlugTypeMap = {
// upcoming
[Certification.RespWebDesignV9]: certTypes.respWebDesignV9,
[Certification.JsV9]: certTypes.javascriptV9
[Certification.JsV9]: certTypes.javascriptV9,
[Certification.A2English]: certTypes.a2English
};
export const superBlockCertTypeMap = {
@@ -229,9 +233,10 @@ export const superBlockCertTypeMap = {
// post-modern
// TODO: use enum
[SuperBlocks.RespWebDesignNew]: certTypes.respWebDesign
[SuperBlocks.RespWebDesignNew]: certTypes.respWebDesign,
// upcoming
[SuperBlocks.A2English]: certTypes.a2English
};
export const certTypeIdMap = {
@@ -255,7 +260,8 @@ export const certTypeIdMap = {
[certTypes.collegeAlgebraPyV8]: certIds.collegeAlgebraPyV8Id,
[certTypes.foundationalCSharpV8]: certIds.foundationalCSharpV8Id,
[certTypes.jsAlgoDataStructV8]: certIds.jsAlgoDataStructV8Id,
[certTypes.javascriptV9]: certIds.javascriptV9Id
[certTypes.javascriptV9]: certIds.javascriptV9Id,
[certTypes.a2English]: certIds.a2EnglishId
};
export const certTypeTitleMap = {
@@ -280,7 +286,8 @@ export const certTypeTitleMap = {
[certTypes.collegeAlgebraPyV8]: 'College Algebra with Python',
[certTypes.foundationalCSharpV8]: 'Foundational C# with Microsoft',
[certTypes.jsAlgoDataStructV8]: 'JavaScript Algorithms and Data Structures',
[certTypes.javascriptV9]: 'JavaScript'
[certTypes.javascriptV9]: 'JavaScript',
[certTypes.a2English]: 'A2 English for Developers'
};
export const superBlockToCertMap: {

View File

@@ -220,6 +220,7 @@ module.exports.fullyCertifiedUser = {
sendQuincyEmail: null,
currentChallengeId: '',
isHonest: true,
isA2EnglishCert: true,
isFrontEndCert: true,
isDataVisCert: true,
isBackEndCert: true,