'use strict'; const tapahtumaTyypit = { lisääAste: 'lisääAste', poistaAste: 'poistaAste', muutaAste: 'muutaAste', lisääLuokka: 'lisääLuokka', poistaLuokka: 'poistaLuokka', lisääOpettaja: 'lisääOpettaja', poistaOpettaja: 'poistaOpettaja', }; class Tapahtuma { constructor(tyyppi, argumentit) { this.tyyppi = tyyppi; this.argumentit = argumentit; } } let historia, tulevaisuus; let luokkaAsteet, opettajat; alustaMalli(); function alustaMalli() { historia = []; tulevaisuus = []; luokkaAsteet = new LuokkaAsteet(); opettajat = new Opettajat(); } function suorita(tyyppi, ...argumentit) { let paluuarvo = undefined; switch (tyyppi) { case tapahtumaTyypit.lisääAste: assertRange('lisääAste argumentit määrä', argumentit.length, 0, 1); paluuarvo = luokkaAsteet.lisää(...argumentit) break; case tapahtumaTyypit.poistaAste: assertEq('poistaAste argumentit määrä', argumentit.length, 1); paluuarvo = luokkaAsteet.poista(...argumentit) break; case tapahtumaTyypit.muutaAste: assertEq('muutaAste argumentit määrä', argumentit.length, 2); luokkaAsteet.muuta(...argumentit) break; case tapahtumaTyypit.lisääLuokka: assertEq('lisääLuokka argumentit määrä', argumentit.length, 1); luokkaAsteet.asteet[argumentit[0]].lisää(); break; case tapahtumaTyypit.poistaLuokka: assertEq('poistaLuokka argumentit määrä', argumentit.length, 1); luokkaAsteet.asteet[argumentit[0]].poista(); break; case tapahtumaTyypit.lisääOpettaja: assertEq('lisääOpettaja argumentit määrä', argumentit.length, 2); opettajat.lisää(argumentit[0], argumentit[1]); break; case tapahtumaTyypit.poistaOpettaja: assertEq('poistaOpettaja argumentit määrä', argumentit.length, 1); opettajat.poista(argumentit[0]); break; default: throw new Error(`tuntematon tapahtumatyyppi ${tyyppi}`); } historia.push(new Tapahtuma(tyyppi, argumentit)); tulevaisuus = []; return paluuarvo; } function kumoa() { if (historia.length === 0) { return; } // Kumoaminen tapahtuu ottamalla historia uusinta tapahtumaa lukuun // ottamatta ja suorittamalla se siihen asti uudestaan tyhjältä mallilta let kumottu = historia.pop(); let uusi_tulevaisuus = tulevaisuus.concat(kumottu); let vanha_historia = historia; alustaMalli(); for (let {tyyppi, argumentit} of vanha_historia) { suorita(tyyppi, ...argumentit); } tulevaisuus = uusi_tulevaisuus; } function teeUudelleen() { if (tulevaisuus.length === 0) { return; } let {tyyppi, argumentit} = tulevaisuus.pop(); // Tulevaisuus tulee tallentaa, sillä suorita() tuhoaa sen let uusi_tulevaisuus = tulevaisuus; suorita(tyyppi, ...argumentit); tulevaisuus = uusi_tulevaisuus; } testi('mallin alustaminen', () => { historia = undefined; tulevaisuus = undefined; luokkaAsteet = undefined; opettajat = undefined; alustaMalli(); assertNe('historia', historia, undefined); assertNe('tulevaisuus', tulevaisuus, undefined); assertNe('luokkaAsteet', luokkaAsteet, undefined); assertNe('opettajat', opettajat, undefined); }); testi('tapahtumahistoria', () => { alustaMalli(); suorita(tapahtumaTyypit.lisääAste); suorita(tapahtumaTyypit.poistaAste, 1); assertEq('historia.length', historia.length, 2); assertEq('historia[0].tyyppi', historia[0].tyyppi, tapahtumaTyypit.lisääAste); assertEq('historia[0].argumentit.length', historia[0].argumentit.length, 0); assertEq('historia[1].tyyppi', historia[1].tyyppi, tapahtumaTyypit.poistaAste); assertEq('historia[1].argumentit.length', historia[1].argumentit.length, 1); assertEq('historia[1].argumentit[0]', historia[1].argumentit[0], 1); assertThrow('poistaAste 1', 'luokka-astetta 1 ei ole olemassa', () => { suorita(tapahtumaTyypit.poistaAste, 1); }); assertEq('historia.length poikkeuksen jälkeen', historia.length, 2); alustaMalli(); }); testi('kumoamiminen', () => { alustaMalli(); kumoa(); suorita(tapahtumaTyypit.lisääAste); suorita(tapahtumaTyypit.lisääLuokka, 1); suorita(tapahtumaTyypit.lisääAste); suorita(tapahtumaTyypit.lisääLuokka, 2); suorita(tapahtumaTyypit.lisääLuokka, 1); assertEq('aluksi 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B', 'C']); assertEq('aluksi 2. aste', luokkaAsteet.asteet[2].luokat(), ['A', 'B']); kumoa(); assertEq('kerran 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); assertEq('kerran 2. aste', luokkaAsteet.asteet[2].luokat(), ['A', 'B']); kumoa(); assertEq('kahdesti 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); assertEq('kahdesti 2. aste', luokkaAsteet.asteet[2].luokat(), ['A']); kumoa(); assertEq('kolmesti 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); assertEq('kolmesti 2. aste', luokkaAsteet.asteet[2], undefined); kumoa(); assertEq('neljästi 1. aste', luokkaAsteet.asteet[1].luokat(), ['A']); kumoa(); assertEq('viidesti 1. aste', luokkaAsteet.asteet[1], undefined); alustaMalli(); }); testi('uudelleen tekeminen', () => { alustaMalli(); teeUudelleen(); suorita(tapahtumaTyypit.lisääAste); suorita(tapahtumaTyypit.lisääAste); suorita(tapahtumaTyypit.lisääLuokka, 1); suorita(tapahtumaTyypit.lisääLuokka, 2); suorita(tapahtumaTyypit.lisääLuokka, 2); kumoa(); kumoa(); kumoa(); assertEq('aluksi 1. aste', luokkaAsteet.asteet[1].luokat(), ['A']); assertEq('aluksi 2. aste', luokkaAsteet.asteet[2].luokat(), ['A']); teeUudelleen(); assertEq('kerran 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); assertEq('kerran 2. aste', luokkaAsteet.asteet[2].luokat(), ['A']); teeUudelleen(); assertEq('kahdesti 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); assertEq('kahdesti 2. aste', luokkaAsteet.asteet[2].luokat(), ['A', 'B']); suorita(tapahtumaTyypit.lisääLuokka, 1); teeUudelleen(); assertEq('kolmesti 1. aste', luokkaAsteet.asteet[1].luokat(), ['A', 'B', 'C']); assertEq('kolmesti 2. aste', luokkaAsteet.asteet[2].luokat(), ['A', 'B']); alustaMalli(); }); testi('asteiden käsittely', () => { alustaMalli(); assertEq('lisää', suorita(tapahtumaTyypit.lisääAste), 1); suorita(tapahtumaTyypit.muutaAste, 1, 2); assertEq('muutettua seuraava aste', luokkaAsteet.seuraavaAste(), 3); suorita(tapahtumaTyypit.poistaAste, 2); assertEq('lopuksi seuraava aste', luokkaAsteet.seuraavaAste(), 1); alustaMalli(); }); testi('luokkien käsittely', () => { alustaMalli(); suorita(tapahtumaTyypit.lisääAste); assertEq('aluksi', luokkaAsteet.asteet[1].luokat(), ['A']); suorita(tapahtumaTyypit.lisääLuokka, 1); assertEq('lisättyä', luokkaAsteet.asteet[1].luokat(), ['A', 'B']); suorita(tapahtumaTyypit.poistaLuokka, 1); assertEq('poistettua', luokkaAsteet.asteet[1].luokat(), ['A']); alustaMalli(); }); testi('opettajien käsittely', () => { alustaMalli(); assertEq('aluksi', opettajat.opettajat(), []); suorita(tapahtumaTyypit.lisääOpettaja, 'MM', 'Maija Meikäläinen'); assertEq('lisättyä', opettajat.opettajat(), [['MM', 'Maija Meikäläinen']]); suorita(tapahtumaTyypit.poistaOpettaja, 'MM'); assertEq('poistettua', opettajat.opettajat(), []); });