Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/challengeView/ChallengeView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useParams } from "react-router-dom"
import { Challenge, PathToChallenge, currentIdFor, getPathToChallenge } from "../../staticData/challenges";
import { Challenge, PathToChallenge, currentIdFor, getPathToChallenge, shouldShowMultipleScenariosButton } from "../../staticData/challenges";
import { Collapse, IconButton, PaperProps, Stack } from "@mui/material";
import MenuIcon from '@mui/icons-material/Menu';
import CloseIcon from '@mui/icons-material/Close';
Expand Down Expand Up @@ -168,7 +168,7 @@ const HorizontalChallengeWorkspace = ({ challenge, blocklyWorkspaceProps }: Chal
</Stack>
<Stack>
<SceneButtons challenge={challenge} running={running} setRunning={setRunning} />
{challenge.shouldShowMultipleScenarioHelp && <MultipleScenariosButton challenge={challenge} disabled={running} />}
{shouldShowMultipleScenariosButton(challenge) && (<MultipleScenariosButton challenge={challenge} disabled={running} />)}
<SceneView descriptor={challenge.sceneDescriptor} />
</Stack>
</Stack>
Expand Down Expand Up @@ -209,7 +209,7 @@ const VerticalChallengeWorkspace = ({ challenge, blocklyWorkspaceProps }: Challe
<SceneView descriptor={challenge.sceneDescriptor} />
<Stack margin='10px' justifyContent='space-between'>
<SceneButtons challenge={challenge} vertical={true} running={running} setRunning={setRunning} />
{challenge.shouldShowMultipleScenarioHelp && <MultipleScenariosButton challenge={challenge} disabled={running} />}
{shouldShowMultipleScenariosButton(challenge) && (<MultipleScenariosButton challenge={challenge} disabled={running} />)}
<InfoButton onClick={() => setOpenDrawer(true)} />
<InfoDrawer open={openDrawer} onClose={() => setOpenDrawer(false)} />
</Stack>
Expand Down
71 changes: 48 additions & 23 deletions src/staticData/challenges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class Challenge {
*/
predefinedSolution?: string

constructor(b: BasicChallenge){
constructor(b: BasicChallenge) {
Object.assign(this, b)
}

Expand All @@ -76,21 +76,21 @@ export const getChallengeWithId = (id: number): Challenge => {

return new Challenge(challenge[0])
}
const legacyChallenges = [{id:1, name:"AlienTocaBoton"}, {id:46, name:"NuevosComandos"}, {id:2, name:"ElGatoEnLaCalle"}, {id:3, name:"NoMeCansoDeSaltar"}, {id:4, name:"ElMarcianoEnElDesierto"}, {id:5, name:"TitoEnciendeLuces"}, {id:6, name:"ElAlienYLasTuercas"}, {id:7, name:"ElRecolectorDeEstrellas"}, {id:8, name:"MariaLaComeSandias"}, {id:9, name:"AlimentandoALosPeces"}, {id:10, name:"InstalandoJuegos"}, {id:11, name:"LaGranAventuraDelMarEncantado"}, {id:12, name:"ReparandoLaNave"}, {id:13, name:"ElMonoYLasBananas"}, {id:14, name:"LaEleccionDelMono"}, {id:15, name:"LaberintoCorto"}, {id:16, name:"TresNaranjas"}, {id:17, name:"TitoRecargado"}, {id:18, name:"LaberintoLargo"}, {id:19, name:"SuperTito1"}, {id:20, name:"SuperTito2"}, {id:21, name:"LaberintoConQueso"}, {id:22, name:"ElDetectiveChaparro"}, {id:23, name:"FutbolRobots"}, {id:24, name:"PrendiendoLasCompus"}, {id:25, name:"ElMonoQueSabeContar"}, {id:26, name:"ElSuperviaje"}, {id:27, name:"ElMonoCuentaDeNuevo"}, {id:28, name:"ElPlanetaDeNano"}, {id:29, name:"DibujandoAlCuadrado"}, {id:30, name:"DibujandoRayuelaRobotica"}, {id:31, name:"DibujandoCortoPorLaDiagonal"}, {id:32, name:"DibujandoMamushkaCuadrada"}, {id:33, name:"DibujandoEscaleraCuadrada"}, {id:34, name:"DibujandoHexagono"}, {id:35, name:"DibujandoPiramideInvertida"}, {id:36, name:"DibujandoFigurasDentroDeFiguras"}, {id:37, name:"DibujandoLaCuevaDeEstalagtitas"}, {id:38, name:"LasRocasDeNano"}, {id:39, name:"LosCaminosDeNano"}, {id:40, name:"UnaFiestaArruinada"}, {id:41, name:"RedecorandoFiestas"}, {id:42, name:"ElDesiertoMultiFrutal"}, {id:43, name:"ElPasilloCurvoDeSandias"}, {id:44, name:"ElFestinFrutal"}, {id:45, name:"RecolectorDeGalaxias"}, {id:130, name:"LaFiestaDeDracula"}, {id:131, name:"SalvandoLaNavidad"}, {id:132, name:"PrendiendoLasCompusParametrizado"}, {id:133, name:"TitoCuadrado"}, {id:134, name:"ElCangrejoAguafiestas"}, {id:135, name:"PrendiendoLasFogatas"}, {id:136, name:"DibujoLibre"}]

const legacyChallenges = [{ id: 1, name: "AlienTocaBoton" }, { id: 46, name: "NuevosComandos" }, { id: 2, name: "ElGatoEnLaCalle" }, { id: 3, name: "NoMeCansoDeSaltar" }, { id: 4, name: "ElMarcianoEnElDesierto" }, { id: 5, name: "TitoEnciendeLuces" }, { id: 6, name: "ElAlienYLasTuercas" }, { id: 7, name: "ElRecolectorDeEstrellas" }, { id: 8, name: "MariaLaComeSandias" }, { id: 9, name: "AlimentandoALosPeces" }, { id: 10, name: "InstalandoJuegos" }, { id: 11, name: "LaGranAventuraDelMarEncantado" }, { id: 12, name: "ReparandoLaNave" }, { id: 13, name: "ElMonoYLasBananas" }, { id: 14, name: "LaEleccionDelMono" }, { id: 15, name: "LaberintoCorto" }, { id: 16, name: "TresNaranjas" }, { id: 17, name: "TitoRecargado" }, { id: 18, name: "LaberintoLargo" }, { id: 19, name: "SuperTito1" }, { id: 20, name: "SuperTito2" }, { id: 21, name: "LaberintoConQueso" }, { id: 22, name: "ElDetectiveChaparro" }, { id: 23, name: "FutbolRobots" }, { id: 24, name: "PrendiendoLasCompus" }, { id: 25, name: "ElMonoQueSabeContar" }, { id: 26, name: "ElSuperviaje" }, { id: 27, name: "ElMonoCuentaDeNuevo" }, { id: 28, name: "ElPlanetaDeNano" }, { id: 29, name: "DibujandoAlCuadrado" }, { id: 30, name: "DibujandoRayuelaRobotica" }, { id: 31, name: "DibujandoCortoPorLaDiagonal" }, { id: 32, name: "DibujandoMamushkaCuadrada" }, { id: 33, name: "DibujandoEscaleraCuadrada" }, { id: 34, name: "DibujandoHexagono" }, { id: 35, name: "DibujandoPiramideInvertida" }, { id: 36, name: "DibujandoFigurasDentroDeFiguras" }, { id: 37, name: "DibujandoLaCuevaDeEstalagtitas" }, { id: 38, name: "LasRocasDeNano" }, { id: 39, name: "LosCaminosDeNano" }, { id: 40, name: "UnaFiestaArruinada" }, { id: 41, name: "RedecorandoFiestas" }, { id: 42, name: "ElDesiertoMultiFrutal" }, { id: 43, name: "ElPasilloCurvoDeSandias" }, { id: 44, name: "ElFestinFrutal" }, { id: 45, name: "RecolectorDeGalaxias" }, { id: 130, name: "LaFiestaDeDracula" }, { id: 131, name: "SalvandoLaNavidad" }, { id: 132, name: "PrendiendoLasCompusParametrizado" }, { id: 133, name: "TitoCuadrado" }, { id: 134, name: "ElCangrejoAguafiestas" }, { id: 135, name: "PrendiendoLasFogatas" }, { id: 136, name: "DibujoLibre" }]

// Legacy challenge ids URLs should be redirected.
const isLegacy = (challengeId: number) => legacyChallenges.find( ({id}) => id === challengeId)
const isLegacy = (challengeId: number) => legacyChallenges.find(({ id }) => id === challengeId)
export const currentIdFor = (challengeId: number) => isLegacy(challengeId) ? challengeId + 1000 : challengeId

/**
@throws Error
**/
export const getChallengeWithName = (challengeName: string): Challenge => {
const challenge = legacyChallenges.find(({name}) => name === challengeName)
if (!challenge) throw new Error(`Challenge with name "${challengeName}" does not exist`)
return getChallengeWithId(currentIdFor(challenge.id))
}
export const getChallengeWithName = (challengeName: string): Challenge => {
const challenge = legacyChallenges.find(({ name }) => name === challengeName)
if (!challenge) throw new Error(`Challenge with name "${challengeName}" does not exist`)
return getChallengeWithId(currentIdFor(challenge.id))
}

export type PathToChallenge = {
book: Book,
Expand All @@ -105,15 +105,40 @@ export const getPathToChallenge = (challengeId: number): PathToChallenge => {
const chapter: Chapter = book.chapters.find(chapter => chapterIncludesChallenge(chapter, challenge))!
const group: Group = chapter.groups.find(group => group.includes(challenge))!

return {book, chapter, group, challenge}
return { book, chapter, group, challenge }
}

export const hasMultipleScenarios = (challenge: Challenge): boolean => {
const descriptor = challenge.sceneDescriptor

const hasArrayWithMultipleStringScenarios =
/new\s+\w+\s*\(\s*\[\s*["'`][\s\S]*?["'`]\s*,\s*["'`]/.test(descriptor)

const hasGenerativeScenario =
descriptor.includes('?') ||
descriptor.includes('|') ||
descriptor.includes('*>') ||
descriptor.includes('#')

return hasArrayWithMultipleStringScenarios || hasGenerativeScenario
}

export const shouldShowMultipleScenariosButton = (challenge: Challenge): boolean => {
if (challenge.shouldShowMultipleScenarioHelp !== undefined) {
return challenge.shouldShowMultipleScenarioHelp
}

return hasMultipleScenarios(challenge)
}

/**
* For testing purposes.
* @returns all sceneDescriptors from existing challenges.
*/
export const allDescriptors = (): Challenge["sceneDescriptor"][] =>
challenges.map( c => c.sceneDescriptor )
challenges.map(c => c.sceneDescriptor)



// This type allows to define a challenge in JSON
// Shouldn't be used outside this file, instead call getChallengeById to get a complete Challenge
Expand Down Expand Up @@ -1516,9 +1541,9 @@ const challenges: BasicChallenge[] = [
sceneDescriptor: `new EscenaCapySolo("[A,-,-,G]")`,
toolboxBlockIds: ['MoverACasillaDerecha', 'SubirPajarito'],
expectations: {
decomposition: false,
simpleRepetition: false
}
decomposition: false,
simpleRepetition: false
}
},
{
id: 1046,
Expand Down Expand Up @@ -2285,7 +2310,7 @@ const challenges: BasicChallenge[] = [
expectations: {
simpleRepetition: false
}
},
},
{
id: 1003,
sceneDescriptor: 'NoMeCansoDeRebotar',
Expand All @@ -2294,7 +2319,7 @@ const challenges: BasicChallenge[] = [
decomposition: false
}
},
{
{
id: 1004,
sceneDescriptor: `new EscenaManic("\
[E,-,E,-,E],\
Expand Down Expand Up @@ -2333,7 +2358,7 @@ const challenges: BasicChallenge[] = [
[-,T,T,T,T],\
[A,T,T,T,T],")`,
toolboxBlockIds: ['MoverACasillaDerecha', 'MoverACasillaArriba', 'VolverABordeIzquierdo', 'RepararTelescopio', 'Procedimiento', 'Repetir']
},
},
{
id: 1008,
sceneDescriptor: `new EscenaManic("\
Expand Down Expand Up @@ -2472,7 +2497,7 @@ const challenges: BasicChallenge[] = [
[*,*,*,*,*],\
", { coleccion: ["G", "A"] })`,
toolboxBlockIds: ['Repetir', 'Si', 'SiNo', 'Hasta', 'Procedimiento',
'VolverAlBordeIzquierdo', 'MoverACasillaDerecha', 'TocandoGuyra','SubirPajarito'],
'VolverAlBordeIzquierdo', 'MoverACasillaDerecha', 'TocandoGuyra', 'SubirPajarito'],
expectations: {
conditionalRepetition: true,
}
Expand Down Expand Up @@ -2526,7 +2551,7 @@ const challenges: BasicChallenge[] = [
expectations: {
decomposition: false
}
},
},
{
id: 1027,
sceneDescriptor: 'ContandoDeNuevoManic',
Expand All @@ -2535,7 +2560,7 @@ const challenges: BasicChallenge[] = [
'ContarPlaneta', 'ContarEstrella', 'TocandoPlaneta',
'TocandoEstrellaManic', 'RepetirVacio', 'Repetir', 'Si', 'SiNo', 'Hasta',
'EstoySobreElInicioManic', 'LargoColumnaActual']
},
},
{
id: 1028,
sceneDescriptor: `new EscenaCapy("\
Expand Down Expand Up @@ -2611,7 +2636,7 @@ const challenges: BasicChallenge[] = [
'[A,_,_,_,_],[-,O,-,-,-],[-,_,_,_,-],[E,-,-,-,-]',
'[A,_,_,_,_],[-,-,-,-,-],[O,_,_,_,-],[E,-,-,-,-]'])`,
toolboxBlockIds: ['Procedimiento', 'MoverACasillaDerecha', 'MoverACasillaArriba', 'MoverACasillaAbajo', 'MoverACasillaIzquierda',
'ObservarEstrella', 'Repetir', 'Si', 'SiNo',
'ObservarEstrella', 'Repetir', 'Si', 'SiNo',
'HayObstaculoArriba', 'HayObstaculoAbajo', 'HayObstaculoIzquierda', 'HayObstaculoDerecha']
},
{
Expand All @@ -2622,7 +2647,7 @@ const challenges: BasicChallenge[] = [
'[A,_,_,_,_,_],[-,-,-,-,-,_],[O,_,_,_,-,_],[-,-,-,-,-,_],[-,_,_,_,_,_],[-,-,O,-,-,-],[_,-,_,_,_,-],[_,E,-,-,-,-]',
'[A,_,_,_,_,_],[-,-,-,-,-,_],[O,_,_,_,-,_],[-,-,-,-,-,_],[-,_,_,_,_,_],[-,-,-,-,-,-],[_,O,_,_,_,-],[_,E,-,-,-,-]'])`,
toolboxBlockIds: ['Procedimiento', 'MoverACasillaDerecha', 'MoverACasillaArriba', 'MoverACasillaAbajo', 'MoverACasillaIzquierda',
'ObservarEstrella', 'Repetir', 'Si', 'SiNo',
'ObservarEstrella', 'Repetir', 'Si', 'SiNo',
'HayObstaculoArriba', 'HayObstaculoAbajo', 'HayObstaculoIzquierda', 'HayObstaculoDerecha'],
expectations: {
decomposition: false,
Expand Down Expand Up @@ -2756,7 +2781,7 @@ const challenges: BasicChallenge[] = [
'MoverA', 'Procedimiento', 'RepetirVacio', 'Repetir', 'Si', 'SiNo', 'Hasta',
'TocandoMariposa', 'FotografiarMariposa', 'Numero', 'OpAritmetica'],
},
{
{
id: 1134,
sceneDescriptor: `new EscenaChuy("\
[A,T,T,T,T,-],\
Expand Down
32 changes: 20 additions & 12 deletions src/test/integration/ChallengeView.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ describe('Challenge view with blocks', () => {
}

const executeChallengeWithEval = (expression: string, expected: any) => {
cy.get('[data-testid="scene-iframe"]').should('have.attr', 'data-loaded', 'true').then($iframe => {
const iframe = $iframe[0] as HTMLIFrameElement;
cy.get('[data-testid="execute-button"]').click().should(() => {
const value = (iframe.contentWindow as any).eval(`pilas.escena_actual().${expression}`);
expect(value).to.equal(expected);
});
})
cy.get('[data-testid="scene-iframe"]')
.should('have.attr', 'data-loaded', 'true')
.then($iframe => {
const iframe = $iframe[0] as HTMLIFrameElement

cy.get('[data-testid="execute-button"]').click()

cy.wrap(null).should(() => {
const value = (iframe.contentWindow as any).eval(`
pilas.escena_actual().${expression}
`)

expect(value).to.equal(expected)
})
})
}

const testExecutionWithBlocks = (name: string, solution: string, expected: any, skip = true) => {
Expand Down Expand Up @@ -238,7 +246,7 @@ describe('Challenge view with blocks', () => {
</block>
</xml>`

const procedureWithParameterSolution = `<xml xmlns="https://developers.google.com/blockly/xml">
const procedureWithParameterSolution = `<xml xmlns="https://developers.google.com/blockly/xml">
<variables>
<variable id="param1">parámetro 1</variable>
</variables>
Expand Down Expand Up @@ -271,15 +279,15 @@ const procedureWithParameterSolution = `<xml xmlns="https://developers.google.co
</block>
</xml>`;

testExecutionWithBlocks('Execution of a solution has effect on scene view', simpleMoveSolution, 0, false)
testExecutionWithBlocks('Execution of a solution has effect on scene view', simpleMoveSolution, 0, false)

//Code from blocks have effect
testExecutionWithBlocks('Code from blocks have effect - boolean operators', operatorSolution, 1, false)
testExecutionWithBlocks('Code from blocks have effect - aritmethic operators', aritmethicSolution, 2, false)

testExecutionWithBlocks('Code from blocks have effect - aritmethic operators', aritmethicSolution, 2, false)

testExecutionWithBlocks('Code from blocks have effect - move with parameters', moveWithParameterSolution, 1, false)

testExecutionWithBlocks('Code from blocks have effect - if', ifSolution, 0, false)

testExecutionWithBlocks('Code from blocks have effect - repeat', repeatSolution, 2, false)
Expand Down
Loading