Tutorial Pharo-Based TDD for Javascript Appsesug.org/data/ESUG2018/02-Tuesday/01-PharoJS...

Preview:

Citation preview

NouryBouraqadi&DaveMasonhttp://pharojs.org

Tu to r i a l Pharo-Based TDD for Javascript Apps

RunonJavascript+

DevelopinPharo=

Tools+

Framework=

MITLicense

Why

•  DevelopinSmalltalkallthetimeJ

•  Portability

•  Front-end/standalone

•  ReuseexistingJSlibraries

•  Run-timeSpeed

?

Javascript0% 100%

Development Production

100% Pharo 0%

DevelopinPharo,RunonJavascript

Javascript0% 100%

Smalltalkclasses

100% Pharo 0%

DevelopinPharo,RunonJavascript

Setofclasses+entrypointclass

Javascript0% 100%

ConvertSmalltalktoJS

100% Pharo 0%

DevelopinPharo,RunonJavascript

Onlyclassesreachablefromthe

entrypointclass

1.WriteTests

3.ExporttoJS2.Passthetests

Lifecyclewith

Javascript0% 100%

100% Pharo 0%

CaseStudy:TDDofaPhysicsSimulation

•  ReuseanexistingJSLibrary•  Testinghybridapp=Pharo+Javascript•  LinkPharocodetoJSobjectsincludingDOM•  Pushingtoproduction

Backlog

•  Atstartup3bodiesè 1floorè 2fallingrectangles

•  "Addxxx"buttonsèAddsabody

•  "Reset"buttonèRemovesallbodies

WriteTests100%Pharo&0%JS

PjWebAppTestCasesubclass:#PhysicsSimTestinstanceVariableNames:''classVariableNames:''package:'ESUG2018'

WriteTests100%Pharo&0%JS

PjWebAppTestCasesubclass:#PhysicsSimTestinstanceVariableNames:''classVariableNames:''package:'ESUG2018'

Appshouldrunonawebbrowser

RunningtheTests~50%Pharo&~50%JS

app

Controller

engine

RunningtheTests~50%Pharo&~50%JS

PhysicsSimTest>>testAppInitialSetupselfassert:appbodiessizeequals:3.

Body1JSObject

Body2JSObject

Body3JSObject

RunningtheTests~50%Pharo&~50%JS

PhysicsSimTest>>testAppInitialSetupselfassert:appbodiessizeequals:3.

InstanceoftheentrypointclassPharoObject

RunningtheTests~50%Pharo&~50%JS

Missingentrypointclass

appClasssubclassResponsibility

DefiningtheEntryPointClass

PjFileBasedWebAppsubclass:#PhysicsSiminstanceVariableNames:'engine'classVariableNames:''package:'ESUG2018'

WerelyonanexternalHTMLfile

TestReferencesEntryPointClass

PhysicsSimTestclass>>appClass^PhysicsSim

Entrypointclass

TestRequestsAppFolder

TestRequestsAppFolder

FolderwhereHTML+otherfiles

arelocated

HTMLFile=App'sView

...<buttonid="resetButton"class="green">Reset</button><divid="simulationView"></div>...<scriptsrc="js/matter.js"></script><scriptsrc="index.js"></script>...

Third-partyJSLibrary

HTMLFile=App'sView

...<buttonid="resetButton"class="green">Reset</button><divid="simulationView"></div>...<scriptsrc="js/matter.js"></script><scriptsrc="js/index.js"></script>...

LinktoJSCodeGeneratedby

PharoJS

HTMLFile=App'sView

...<buttonid="resetButton"class="green">Reset</button><divid="simulationView"></div>...<scriptsrc="js/matter.js"></script><scriptsrc="js/index.js"></script>...

NoBehavior!

HTMLFile=App'sView

...<buttonid="resetButton"class="green">Reset</button><divid="simulationView"></div>...<scriptsrc="js/matter.js"></script><scriptsrc="js/index.js"></script>...

NoBehavior!

ID=toattachPharocode

RerunTests

PharoJSOpensHTMLfileinawebbrowser

RerunTests

PharoJSOpensHTMLfileinawebbrowser

NoSimulation!

RerunTests

FixingtheApp

PhysicsSim>>initializesuperinitialize.selfcreateAndStartEngine.selfsetupAndStartRendering.

PhysicsSim>>bodies^engineworldbodies

JSDoc/Examples PharoCode

FixingtheApp

PhysicsSim>>matterJsRoot^windowMatter

PhysicsSim>>createAndStartEngine|runner|engine:=selfmatterJsRootEnginecreate.runner:=selfmatterJsRootRunnercreate.selfmatterJsRootRunnerrun:runnerwith:engine

ProxytoJavascriptglobal

FixingtheApp

PhysicsSim>>matterJsRoot^windowMatter

PhysicsSim>>createAndStartEngine|runner|engine:=selfmatterJsRootEnginecreate.runner:=selfmatterJsRootRunnercreate.selfmatterJsRootRunnerrun:runnerwith:engine

PharoJSgeneratedaccessortoJSobjectproperty

FixingtheApp

PhysicsSim>>matterJsRoot^windowMatter

PhysicsSim>>createAndStartEngine|runner|engine:=selfmatterJsRootEnginecreate.runner:=selfmatterJsRootRunnercreate.selfmatterJsRootRunnerrun:runnerwith:engine

CallingJSmethodcreate()

FixingtheApp

PhysicsSim>>matterJsRoot^windowMatter

PhysicsSim>>createAndStartEngine|runner|engine:=selfmatterJsRootEnginecreate.runner:=selfmatterJsRootRunnercreate.selfmatterJsRootRunnerrun:runnerwith:engine

CallingJSmethodrun(runner,engine)

FixingtheAppPhysicsSim>>setupAndStartRendering|rendersimulationView|simulationView:=selfelementAt:#simulationView.render:=selfmatterJsRootRendercreate: {#element->simulationView. #engine->engine. #options->{#width->800. #height->600. #wireframes->false. #background->'transparent'}}asJsObject.

selfmatterJsRootRenderrun:render

FixingtheAppPhysicsSim>>setupAndStartRendering|rendersimulationView|simulationView:=selfelementAt:#simulationView.render:=selfmatterJsRootRendercreate: {#element->simulationView. #engine->engine. #options->{#width->800. #height->600. #wireframes->false. #background->'transparent'}}asJsObject.

selfmatterJsRootRenderrun:render

FindstheDOMelementwithID"SimulationView"

HTML

...<divid="simulationView"></div>...

FixingtheAppPhysicsSim>>setupAndStartRendering|rendersimulationView|simulationView:=selfelementAt:#simulationView.render:=selfmatterJsRootRendercreate: {#element->simulationView. #engine->engine. #options->{#width->800. #height->600. #wireframes->false. #background->'transparent'}}asJsObject.

selfmatterJsRootRenderrun:render

CreateaJSObjectfromaPharoDictionary

DefiningLiteralJSObjectsinPharo

{#element->simulationView.#engine->engine.#options->{#width->800.#height->600.#wireframes->false.#background->'transparent'}

}asJsObject.

JSDoc/Examples PharoCode

{element:simulationView.engine:engine.options:{width:800.height:600.wireframes:false.background:'transparent'}

}

Javascriptcode Pharoequivalent

RerunTests

FixingtheApp

PhysicsSim>>initializesuperinitialize.selfcreateAndStartEngine.selfsetupAndStartRendering.selfaddInitialBodies

Bodies.rectangle(400,610,810,60,{isStatic:true});

CreatingMatterJSBodies

selfmatterJsRootBodiesrectangle:400y:610width:810height:60options:{ #isStatic->true}asJsObject.

JSDoc/Examples PharoCode

Javascriptcode Pharoequivalent

1stTestPasses!

Morefeatures=ButtonHandling

Reset=DeleteMobileBodies

HTMLIDforLinkingwithPharo

HTML...<buttonid="resetButton">Reset</button>...

WriteTestsforResetButton

PhysicsSimTest>>testResetDeleteMobileBodiesOnlyselfclickElementById:#resetButton.selfassert:appbodiessizeequals:1.

HTML...<buttonid="resetButton">Reset</button>...

WriteTestsforResetButton

PhysicsSimTest>>testResetDeleteMobileBodiesOnlyselfclickElementById:#resetButton.selfassert:appbodiessizeequals:1.

HTML...<buttonid="resetButton">Reset</button>...

PharotriggerseventonDOMobject!

TestFails

AddBehaviortoHTMLButton

PhysicsSim>>initialize...selfonClick:#resetButton do:[selfmatterJsRootWorld clear:engineworld keepStatic:true]

AddBehaviortoHTMLButton

PhysicsSim>>initialize...selfonClick:#resetButton do:[selfmatterJsRootWorld clear:engineworld keepStatic:true]

HTMLID

AddBehaviortoHTMLButton

PhysicsSim>>initialize...selfonClick:#resetButton do:[selfmatterJsRootWorld clear:engineworld keepStatic:true]

PharoBlockCalling

MatterJSLibrary

AddBehaviortoHTMLButton

ValuePharoBlock

ClickonHTMLButton

Lifecyclewith

Javascript0% 100%

100% Pharo 0%

3.ExporttoJS

1.WriteTests2.Passthetests

ExportAppforProduction100%Javascript

ExportAppforProduction100%Javascript

ExportAppforProduction100%Javascript

ExportedJSfile

ExportedJSfile

...<scriptsrc="js/matter.js"></script><scriptsrc="js/index.js"></script>...

http://pharojs.org/Demo/MatterJsDemo/index.html

BeyondResearch!

OpenSourcefortheRealWorld

PharoTradition!

nootrix.com

Farmers Market

nootrix.com

GPSNavigation

ZoomableMap

UpdateMarkers

WhatNext?

•  Bettersupportformobileapps•  MigratetoPharo7

•  ImproveCodeExtraction

•  Middlewareforhybridapps

Thisafternoon!

LearnmoreaboutPharoJS•  Web:http://pharojs.org– FAQ+...– ThanksESUGforthesupport

•  Slack:https://pharojs.slack.com/– Discussions

•  Twitter:https://twitter.com/pharojs– News– SubscriptiontoPharoJSSlack

NouryBouraqadi&DaveMason

DevelopinPharoRunonJavascript

http://pharojs.org