diff --git a/examples/run-examples/basic-game/.github/workflows/ci.yml b/examples/run-examples/basic-game/.github/workflows/ci.yml new file mode 100644 index 0000000..8e84736 --- /dev/null +++ b/examples/run-examples/basic-game/.github/workflows/ci.yml @@ -0,0 +1,14 @@ +name: build + +on: [push, pull_request] +jobs: + wollok-ts: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - run: | + wget -O wollok-ts-cli https://github.com/uqbar-project/wollok-ts-cli/releases/latest/download/wollok-ts-cli-linux-x64 + chmod a+x ./wollok-ts-cli + ./wollok-ts-cli test --skipValidations -p ./ + shell: bash diff --git a/examples/run-examples/basic-game/README.md b/examples/run-examples/basic-game/README.md new file mode 100644 index 0000000..76b305e --- /dev/null +++ b/examples/run-examples/basic-game/README.md @@ -0,0 +1,6 @@ + + +## example + +TODO + diff --git a/examples/run-examples/basic-game/example.wlk b/examples/run-examples/basic-game/example.wlk new file mode 100644 index 0000000..4be5144 --- /dev/null +++ b/examples/run-examples/basic-game/example.wlk @@ -0,0 +1,14 @@ +object pepita { + var energy = 100 + + method energy() = energy + + method eat(grams) { + energy = energy + grams * 10 + } + + method fly(minutes) { + energy = energy - minutes * 3 + } + +} \ No newline at end of file diff --git a/examples/run-examples/basic-game/mainExample.wpgm b/examples/run-examples/basic-game/mainExample.wpgm new file mode 100644 index 0000000..dc75868 --- /dev/null +++ b/examples/run-examples/basic-game/mainExample.wpgm @@ -0,0 +1,14 @@ +import example.pepita + +program PepitaProgram { + + pepita.fly(10) + console.println("Pepita empieza con " + pepita.energy()) + console.println("Vuela") + pepita.fly(10) + console.println(pepita.energy()) + console.println("Come") + pepita.eat(25) + console.println(pepita.energy()) + +} \ No newline at end of file diff --git a/examples/run-examples/basic-game/mainGame.wpgm b/examples/run-examples/basic-game/mainGame.wpgm new file mode 100644 index 0000000..01573ad --- /dev/null +++ b/examples/run-examples/basic-game/mainGame.wpgm @@ -0,0 +1,32 @@ +object pepita { + var energy = 100 + var nombre = "Pepita" + var property image = "pepita.png" + var property position = new MutablePosition(x = 0, y = 1) + var height = 10 + var width = 10 + + method energy() = energy + + method eat(grams) { + energy = energy + grams * 10 + } + + method fly(minutes) { + energy = energy - minutes * 3 + } + +} + +program PepitaGame { + game.addVisualCharacter(pepita) + + // Stop on connection (from any client) + game.onTick(0, "end", { + console.println("finishing...") + game.stop() + }) + + console.println("starting...") + game.start() +} diff --git a/examples/run-examples/basic-game/package.json b/examples/run-examples/basic-game/package.json new file mode 100644 index 0000000..e662fff --- /dev/null +++ b/examples/run-examples/basic-game/package.json @@ -0,0 +1,8 @@ +{ + "name": "existing-folder", + "version": "1.0.0", + "resourceFolder": "specialAssets", + "wollokVersion": "4.0.0", + "author": "dodain", + "license": "ISC" +} diff --git a/examples/run-examples/basic-game/specialAssets/pepita.png b/examples/run-examples/basic-game/specialAssets/pepita.png new file mode 100644 index 0000000..bb052db Binary files /dev/null and b/examples/run-examples/basic-game/specialAssets/pepita.png differ diff --git a/examples/run-examples/basic-game/testExample.wtest b/examples/run-examples/basic-game/testExample.wtest new file mode 100644 index 0000000..7886ddb --- /dev/null +++ b/examples/run-examples/basic-game/testExample.wtest @@ -0,0 +1,9 @@ +import example.pepita + +describe "group of tests for pepita" { + + test "pepita has initial energy" { + assert.equals(100, pepita.energy()) + } + +} \ No newline at end of file diff --git a/package.json b/package.json index f8a8c62..7f5735c 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,12 @@ "prepare": "node ./scripts/download-libs.js && npm run build:tools", "start": "node ./build/src/index.js", "test": "npm run lint && npm run test:unit", - "test:diagram": "mocha -r ts-node/register/transpile-only test/**/diagram.test.ts --timeout 7000", + "test:diagram": "mocha -r ts-node/register/transpile-only test/**/diagram.test.ts --timeout 5000", + "test:run": "mocha -r ts-node/register/transpile-only test/**/run.test.ts --timeout 2000", "test-with-coverage": "npm run lint && nyc --reporter=json --lines 70 npm run test:unit", "lint": "eslint . ", "lint:fix": "eslint . --fix", - "test:unit": "mocha --parallel -r ts-node/register/transpile-only test/**/*.test.ts --timeout 7000", + "test:unit": "mocha --parallel -r ts-node/register/transpile-only test/**/*.test.ts --timeout 5000", "build:tools": "shx cp ./node_modules/wollok-web-tools/dist/web/game-index.js ./public/game/lib && shx cp ./node_modules/wollok-web-tools/dist/dynamicDiagram/diagram-index.js ./public/diagram", "build": "shx rm -rf build && shx mkdir ./build && shx cp -r ./public ./build/public && tsc -p ./tsconfig.build.json", "watch": "npm run build -- -w", diff --git a/src/commands/run.ts b/src/commands/run.ts index d03c012..677644c 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -21,7 +21,7 @@ export type Options = { host: string, port: string game: boolean, - startDiagram: boolean + startDiagram: boolean, } const DEFAULT_PORT = '4200' @@ -31,8 +31,8 @@ type DynamicDiagramClient = { onReload: () => void, } -export default async function (programFQN: Name, options: Options): Promise { - const { project, game } = options +export default async function (programFQN: Name, options: Options): Promise { + const { game, project, assets } = options const timeMeasurer = new TimeMeasurer() try { logger.info(`${game ? gameIcon : programIcon} Running program ${valueDescription(programFQN)} ${runner(game)} on ${valueDescription(project)}`) @@ -52,16 +52,18 @@ export default async function (programFQN: Name, options: Options): Promise(programFQN).parent as Package const dynamicDiagramClient = await initializeDynamicDiagram(programPackage, options, interpreter) + const ioGame: Server | undefined = initializeGameClient(options) interpreter.run(programFQN) - eventsFor(ioGame!, interpreter, dynamicDiagramClient, options) + if (game) { + eventsFor(ioGame!, interpreter, dynamicDiagramClient, assetFiles) + } if (debug) timeEnd(successDescription('Run finalized successfully')) @@ -69,6 +71,8 @@ export default async function (programFQN: Name, options: Options): Promise { - if (!game) return - const baseFolder = join(project, assets) - if (!existsSync(baseFolder)) - logger.warn(failureDescription(`Resource folder for images not found: ${assets}`)) - - - const assetFiles = getAllAssets(project, assets) - +export const eventsFor = (io: Server, interpreter: Interpreter, dynamicDiagramClient: DynamicDiagramClient, assetFiles: Asset[]): void => { io.on('connection', socket => { logger.info(successDescription('Running game!')) socket.on('keyPressed', (events: string[]) => { @@ -211,7 +207,7 @@ export const eventsFor = (io: Server, interpreter: Interpreter, dynamicDiagramCl export const getAllAssets = (projectPath: string, assetsFolder: string): Asset[] => { const baseFolder = join(projectPath, assetsFolder) if (!existsSync(baseFolder)) - throw `Folder image ${baseFolder} does not exist` + throw new Error(`Folder image ${baseFolder} does not exist`) const fileRelativeFor = (fileName: string) => ({ name: fileName, url: fileName }) diff --git a/src/index.ts b/src/index.ts index 80ffc44..708e90b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,7 @@ program.command('run') .option('-g, --game', 'sets the program as a game', false) .option('-v, --verbose', 'print debugging information', false) .option('-d, --startDiagram', 'activate the dynamic diagram (only for games)', false) - .action(run) + .action((programFQN, options) => { run(programFQN, options) }) program.command('test') .description('Run Wollok tests') diff --git a/test/diagram.test.ts b/test/diagram.test.ts index 887fec0..24ba8b9 100644 --- a/test/diagram.test.ts +++ b/test/diagram.test.ts @@ -19,7 +19,7 @@ describe('Dynamic diagram', () => { port: '8080', host: 'localhost', darkMode: true, - skipDiagram: false, + skipDiagram: true, // we don't want to open a socket } let interpreter: Interpreter diff --git a/test/dynamicDiagramClient.test.ts b/test/dynamicDiagramClient.test.ts index 7aca140..b8a254b 100644 --- a/test/dynamicDiagramClient.test.ts +++ b/test/dynamicDiagramClient.test.ts @@ -36,10 +36,13 @@ describe('dynamic diagram client', () => { it('should work for root path', async () => { const { enabled, app, server } = await initializeClient(options, repl, interpreter) - expect(enabled).to.be.true - const result = await chai.request(app).get('/index.html') - expect(result).to.have.status(200) - server!.close() + try { + expect(enabled).to.be.true + const result = await chai.request(app).get('/index.html') + expect(result).to.have.status(200) + } finally { + server!.close() + } }) it('should return a fake client if skipDiagram is set', async () => { diff --git a/test/fullRepl.test.ts b/test/fullRepl.test.ts index 93eb427..068b49f 100644 --- a/test/fullRepl.test.ts +++ b/test/fullRepl.test.ts @@ -30,10 +30,6 @@ const buildOptionsFor = (path: string, skipValidations = false) => ({ const callRepl = (autoImportPath: string, options: Options) => replFn(join(options.project, autoImportPath), options) -// Be careful, if you are in developing mode -// and some of these tests fail it will lead to exit code 13 -// because an active session of the dynamic diagram -// will remain running in background describe('REPL integration test for valid project', () => { const projectPath = join('examples', 'repl-examples') @@ -43,7 +39,7 @@ describe('REPL integration test for valid project', () => { darkMode: true, host: 'localhost', port: '8080', - skipDiagram: false, + skipDiagram: true, } let processExitSpy: sinon.SinonStub let consoleLogSpy: sinon.SinonStub diff --git a/test/repl.test.ts b/test/repl.test.ts index c8e806d..4f1dfbf 100644 --- a/test/repl.test.ts +++ b/test/repl.test.ts @@ -16,7 +16,7 @@ describe('REPL', () => { darkMode: true, port: '8080', host: 'localhost', - skipDiagram: false, + skipDiagram: true, } let interpreter: Interpreter diff --git a/test/run.test.ts b/test/run.test.ts index 3055d0a..7193cd3 100644 --- a/test/run.test.ts +++ b/test/run.test.ts @@ -1,14 +1,17 @@ import chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import chaiHttp from 'chai-http' import { mkdirSync, rmdirSync } from 'fs' import { join } from 'path' import sinon from 'sinon' -import { io as ioc } from 'socket.io-client' +import * as utils from '../src/utils' import run, { buildEnvironmentForProgram, getAllAssets, getAssetsFolder, getGameInterpreter, getSoundsFolder, getVisuals, Options } from '../src/commands/run' import { logger as fileLogger } from '../src/logger' import { spyCalledWithSubstring } from './assertions' - chai.should() +chai.use(chaiHttp) +chai.use(chaiAsPromised) const expect = chai.expect const project = join('examples', 'run-examples', 'basic-example') @@ -216,20 +219,53 @@ describe('testing run', () => { }) describe('run a simple game', () => { + let handleErrorSpy: sinon.SinonStub + let processExitSpy: sinon.SinonStub + let errorReturned: string | undefined = undefined + + beforeEach(() => { + handleErrorSpy = sinon.stub(utils, 'handleError') + handleErrorSpy.callsFake((error) => { + console.info(`👾👾👾 ${error.message} 👾👾👾`) + errorReturned = error.message + }) + processExitSpy = sinon.stub(process, 'exit') + }) + + afterEach(() => { + sinon.restore() + }) - it('smoke test - should work if program has no errors', done => { - run('mainGame.PepitaGame', { + it('smoke test - should work if program has no errors', async () => { + const ioGame = await run('mainGame.PepitaGame', { + project: join('examples', 'run-examples', 'basic-game'), + skipValidations: false, + game: true, + startDiagram: false, + assets: 'specialAssets', + port: '3000', + host: 'localhost', + }) + ioGame?.close() + expect(processExitSpy.calledWith(0)).to.be.false + expect(errorReturned).to.be.undefined + }) + + it('smoke test - should not work if program has errors', async () => { + const ioGame = await run('mainGame.PepitaGame', { project: join('examples', 'run-examples', 'basic-example'), skipValidations: false, game: true, startDiagram: false, - assets, + assets: 'specialAssets', port: '3000', host: 'localhost', }) - const clientSocket = ioc('http://localhost:3000') - clientSocket.on('connect', done) // Game finish on client connection + ioGame?.close() + expect(processExitSpy.calledWith(21)).to.be.false + expect(errorReturned).to.equal('Folder image examples/run-examples/basic-example/specialAssets does not exist') }) + }) }) \ No newline at end of file