CLASS TestNwApp EXTENDS Test MODULE TestNwjs CLASSVARS '' VARS 'app'
"Also tests NwManifest"
test
| manifest hotKey |
app := NwApp new.
self assert: [ app class = NwApp ].
app clearCache.
self assert: [ app argv includes: '-test' ].
self assert: [ app fullArgv includes: '-test' ].
self assert: [ app dataPath includes: 'my-nw-app' ].
"'app quit' cannot be tested here."
self assert: [ ( app getProxyForUrl: 'localhost:8080' ) = 'DIRECT' ].
app addOriginAccessWhitelistEntry: 'http://github.com/'
destinationProtocol: 'chrome-extension' destinationHost: 'http://localhost'
allowDestinationSubdomains: true.
app removeOriginAccessWhitelistEntry: 'http://github.com/'
destinationProtocol: 'chrome-extension' destinationHost: 'http://localhost'
allowDestinationSubdomains: true.
"Manifest"
manifest := app manifest.
self assert: [ manifest class = NwManifest ].
self assert: [ manifest name = 'my-nw-app' ].
self assert: [ manifest main endsWith: 'index.html' ].
"Hotkey"
hotKey := 'Ctrl+Shift+A'.
app registerGlobalHotKey: hotKey
then: [ self onRegisterGlobalHotKey: hotKey ]
error: [ :message | self onRegisterGlobalHotKey: hotKey error: message].
"closeAllWindows cannot be tested here beacuse it terminates the app."
"app closeAllWindows."
!
"Hotkeys"
onRegisterGlobalHotKey: hotKey
self assert: [ true ].
self unregisterGlobalHotKey: hotKey
!
onRegisterGlobalHotKey: hotKey error: message
"Failing to register the hotkey also counts als test succes.
Probably some security setting."
self assert: [ true ].
self unregisterGlobalHotKey: hotKey
!
unregisterGlobalHotKey: hotKey
app unregisterGlobalHotKey: hotKey
then: [ self onUnegisterGlobalHotKey: hotKey ]
error: [ :message | self onUnregisterGlobalHotKey: hotKey error: message] .
!
onUnregisterGlobalHotKey: hotKey
self assert: [ true ].
!
onUnregisterGlobalHotKey: hotKey error: message
"Failing to unregister the hotkey also counts als test succes.
Probably some security setting."
self assert: [ true ].
!
CLASS TestNwMenu EXTENDS Test MODULE TestNwjs CLASSVARS '' VARS ''
test
| menu menuItem |
menu := NwMenu newMenuBar.
self assert: [ menu class = NwMenu ].
self assert: [ menu type = 'menubar' ].
menu := NwMenu new.
self assert: [ menu class = NwMenu ].
self assert: [ menu type = 'contextmenu' ].
self assert: [ menu items length = 0 ].
menuItem := NwMenuItem new: 'Edit'.
menu append: menuItem.
self assert: [ menu items length = 1 ].
self assert: [ menu items first label = 'Edit' ].
menu remove: menuItem.
self assert: [ menu items length = 0 ].
menuItem := NwMenuItem new: 'Item2'.
menu append: menuItem.
menuItem := NwMenuItem new: 'Item1'.
menu insert: menuItem at: 0.
self assert: [ menu items length = 2 ].
self assert: [ menu items first label = 'Item1' ].
menu removeAt: 0.
self assert: [ menu items length = 1 ].
self assert: [ menu items first label = 'Item2' ].
!
CLASS TestNwMenuItem EXTENDS Test MODULE TestNwjs CLASSVARS '' VARS ''
test
| menuItem submenu |
menuItem := NwMenuItem new: 'File'.
self assert: [ menuItem type = 'normal' ].
self assert: [ menuItem label = 'File' ].
menuItem label: 'File2'.
self assert: [ menuItem label = 'File2' ].
self assert: [ menuItem submenu isNil ].
submenu := NwMenu new.
menuItem submenu: submenu.
self assert: [ menuItem submenu = submenu ].
self assert: [ menuItem icon = '' ].
menuItem icon: 'icon.png'.
self assert: [ menuItem icon = 'icon.png' ].
self assert: [ menuItem tooltip = '' ].
menuItem tooltip: 'My tooltip'.
self assert: [ menuItem tooltip = 'My tooltip' ].
self assert: [ menuItem click isNil ].
menuItem click: [ self onMenuItemClick ].
self assert: [ menuItem click notNil ].
self assert: [ menuItem enabled ].
menuItem enabled: false.
self assert: [ menuItem enabled not ].
self assert: [ menuItem key = '' ].
"2025-08-14 Setting the menu item key crashes Nw.js on Windows:
This has been reported here: https://github.com/nwjs/nw.js/issues/8290
menuItem key: 'F'.
self assert: [ menuItem key = 'F1' ]."
self assert: [ menuItem modifiers = '' ].
"2025-08-14 Setting the menu item modifiers also crashes Nw.js on Windows:
This has been reported here: https://github.com/nwjs/nw.js/issues/8290
menuItem modifiers: 'alt'.
self assert: [ menuItem modifiers = 'alt' ]."
"Checkbox"
menuItem := NwMenuItem checkbox: 'Private'.
self assert: [ menuItem type = 'checkbox' ].
self assert: [ menuItem label = 'Private' ].
self assert: [ menuItem checked not ].
menuItem checked: true.
self assert: [ menuItem checked ].
"Separator"
menuItem := NwMenuItem separator.
self assert: [ menuItem type = 'separator' ].
!
CLASS TestNwWindow EXTENDS Test MODULE TestNwjs CLASSVARS '' VARS 'window'
"NwWindow is tested in TestMyNwWindow with a live Window."
CLASS TestQLabel EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| label |
label := QLabel new.
self assert: [ label jsClassName = 'QLabel' ].
label setText: 'My Label'.
self assert: [ label text = 'My Label' ].
!
CLASS TestQMainWindow EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| window menuBar centralWidget |
window := QMainWindow new.
self assert: [ window jsClassName = 'QMainWindow' ].
menuBar := QMenuBar new.
window setMenuBar: menuBar.
self assert: [ window menuBar = menuBar ].
centralWidget := QWidget new.
window setCentralWidget: centralWidget.
self assert: [ window centralWidget = centralWidget ].
!
CLASS TestQWidget EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| widget |
widget := QWidget new.
self assert: [ widget jsClassName = 'QWidget' ].
widget setWindowTitle: 'My Window Title'.
self assert: [ widget windowTitle = 'My Window Title' ].
widget setStyleSheet: '#root { align-items: "center" }'.
self assert: [ widget styleSheet startsWith: '#root' ].
widget setWindowIcon: QIcon new.
self assert: [ widget windowIcon jsClassName = 'QIcon' ].
!
CLASS TestQMenuBar EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
"Also tests QMenu and QAction"
test
| action menu menuBar |
"========================= Action"
action := QAction new.
self assert: [ action jsClassName = 'QAction' ].
action setText: 'My Action'.
"QAction.text() not implemented in NodeGui yet (5-OCT-2024)"
"self assert: [ action text = 'My Action' ]."
action addEventListener: 'triggered' then: [ self onAction ].
"QAction.activate() not implemented in NodeGui yet (5-OCT-2024)"
"action activate."
"========================= Menu"
menu := QMenu new.
self assert: [ menu jsClassName = 'QMenu' ].
menu setTitle: 'My Title'.
self assert: [ menu title = 'My Title' ].
menu addAction: action.
self assert: [ menu menuAction jsClassName = 'QAction' ].
"========================= MenuBar"
menuBar := QMenuBar new.
self assert: [ menuBar jsClassName = 'QMenuBar' ].
menuBar addMenu: menu.
!
onAction
"Never called."
self assert: [ true ].
!
CLASS TestQBoxLayout EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| layout |
layout := QBoxLayout new: QLayout topToBottom.
self assert: [ layout class = QBoxLayout ].
layout addWidget: QLabel new stretch: 0 align: QLayout alignCenter.
self assert: [ layout count = 1 ].
!
CLASS TestQIcon EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| icon |
icon := QIcon new: 'missing.ico'.
self assert: [ icon name = '' ].
!
CLASS TestQPixmap EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| pixmap |
pixmap := QPixmap new: 'missing.png'.
self assert: [ pixmap class = QPixmap ].
!
CLASS TestQPushButton EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
"Also tests QAbstractButton"
test
| button |
button := QPushButton new.
button setText: 'My Button'.
self assert: [ button text = 'My Button' ].
button onClick: [ self buttonClicked ].
button click.
!
buttonClicked
self assert: [ true ].
!
CLASS TestQApplication EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| qApplication |
qApplication := QApplication instance.
self assert: [ qApplication jsClassName = 'QApplication' ].
self assert: [ qApplication testMode | true ].
!
CLASS TestQObject EXTENDS Test MODULE TestNodeGui CLASSVARS '' VARS ''
test
| object |
object := QObject new.
self assert: [ object jsClassName = 'QObject' ].
object setObjectName: 'myObjectName'.
self assert: [ object objectName = 'myObjectName' ].
!
CLASS MyNodeWorker EXTENDS Object MODULE TestNode CLASSVARS '' VARS ''
METHODS
start
NodeMessagePort parentPort on: 'message' class: String
then: [ :message | self onMessage: message ].
self assert: [ NodeMessagePort isMainThread not ].
NodeMessagePort setEnvironmentData: 'MyWorker' to: 'Saved'.
self assert: [ ( NodeMessagePort getEnvironmentData: 'MyWorker' ) = 'Saved' ].
!
onMessage: message
self assert: [ message = 'Hello, worker!' ].
NodeMessagePort parentPort postMessage: 'Hello, main thread!'.
!
CLASS TestNodeMessagePort EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
!
CLASS TestNodeWorker EXTENDS Test MODULE TestNode CLASSVARS '' VARS 'worker'
METHODS
test
worker := NodeWorker new: './out/worker.js'.
self assert: [ worker threadId >= 0 ].
worker on: 'message' class: String then: [ :message | self onMessage: message ].
worker on: 'messageerror' class: Error then: [ :error | self onMessageError: error ].
worker on: 'error' class: Error then: [ :error | self onError: error ].
worker on: 'exit' class: Integer then: [ :code | self onExit: code ].
worker postMessage: 'Hello, worker!'.
!
onMessage: message
self assert: [ message = 'Hello, main thread!' ].
self terminate.
!
onMessageError: error
self error: 'TestNodeWorker: NodeWorker massage error: ', error message.
!
onError: error
self error: 'TestNodeWorker: NodeWorker error: ', error message.
!
terminate
worker ref.
worker unref.
worker terminate.
!
onExit: code
self assert: [ code >= 0 ].
!
CLASS TestExpress EXTENDS Test MODULE TestServer CLASSVARS '' VARS 'express server sessionCookie'
"Also tests class Server."
METHODS
test
express := Express new.
express useSession.
express get: '/login'
then: [ :request :response | self onLoginRequest: request response: response ].
express get: '/products'
then: [ :request :response | self onProductsRequest: request response: response ].
server := express listen: 3000
then: [ :error | self onExpressListen: error ].
!
async onExpressListen: error
error isNil ifFalse: [ error throw ].
await self requestLogin.
await self requestProducts.
server terminate.
!
"=============================== Login"
async requestLogin
| url response text |
url := 'http://localhost:3000/login?name=John&password=secret'.
response := await Fetch request: url.
text := await response text.
self assert: [ text = 'Login succeeded' ].
sessionCookie := response cookie.
self assert: [ sessionCookie includes: 'connect.sid' ].
!
onLoginRequest: request response: response
| name password |
name := request query atProperty: 'name'.
self assert: [ name = 'John' ].
password := request query atProperty: 'password'.
self assert: [ password = 'secret' ].
request session set: 'loggedIn' to: true.
response send: 'Login succeeded'.
!
"=============================== Products"
async requestProducts
| headers options url text |
headers := Headers new
set: 'cookie' value: sessionCookie.
options := RequestInit new
headers: headers.
url := 'http://localhost:3000/products'.
text := await Fetch text: url options: options.
self assert: [ text = 'Apple, Orange, Pear' ].
!
onProductsRequest: request response: response
| loggedIn |
loggedIn := ( request session get: 'loggedIn' ) = true.
loggedIn
ifFalse: [ response send: 'Not logged in' ]
ifTrue: [ response send: 'Apple, Orange, Pear' ].
!
CLASS TestCpuUsage EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| cpuUsage |
cpuUsage := Process cpuUsage.
self assert: [ cpuUsage user >= 0 ].
self assert: [ cpuUsage system >= 0 ].
cpuUsage := Process threadCpuUsage.
self assert: [ cpuUsage user >= 0 ].
self assert: [ cpuUsage system >= 0 ].
!
CLASS TestEnvironment EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| var data |
var := 'SMALLJS_TEMP'.
data := 'EnvData'.
Environment at: var put: data.
self assert: [ ( Environment at: var ) = data ].
Environment deleteAt: var.
self assert: [ ( Environment at: var ) isNil ].
!
testLoad
| tempPath envString |
tempPath := Os tmpPath: 'smalljs-env-'.
envString := 'SMALLJS_LOADED=loaded\n'.
Fs writeFileSync: tempPath data: envString options: nil.
Environment load: tempPath.
self assert: [ ( Environment at: 'SMALLJS_LOADED' ) = 'loaded' ].
Fs unlinkSync: tempPath.
!
CLASS TestMemoruUsage EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| memoryUsage |
memoryUsage := Process memoryUsage.
self assert: [ memoryUsage class = MemoryUsage ].
self assert: [ memoryUsage rss > 1000 ].
self assert: [ memoryUsage heapTotal > 1000 ].
self assert: [ memoryUsage heapUsed > 1000 ].
self assert: [ memoryUsage external > 1000 ].
self assert: [ memoryUsage arrayBuffers > 1000 ].
!
CLASS TestProcess EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
testArguments
| temp |
self assert: [ Process argv length >= 2 ].
self assert: [ Process argv first includes: 'node' ].
self assert: [ Process argv0 includes: 'node' ].
self assert: [ Process execPath includes: 'node' ].
self assert: [ Process execArgv length = 0 ].
!
testDirectory
| cwd chdir |
cwd := Process cwd.
self assert: [ ( cwd includes: '/' ) | ( cwd includes: '\\' ) ].
chdir := Path join: cwd with: 'out'.
Process chdir: chdir.
self assert: [ Process cwd = chdir ].
Process chdir: cwd.
!
testExiting
"Only test these manually, because they exit..."
"Process exit: 9"
"Process abort."
"Process kill: Process pid signal: 15."
"To test this in VSCode, in Run and Debug, Breakpoints: 'Uncaught Exceptions' must be unchecked"
"Process uncaughtExceptionCaptureCallback:
[ :error | self onUncaughtException: error ].
self causeUncaughtException."
!
onUncaughtException: error
Console log: 'TestProcess: My uncaught exception handler'.
Process exit: 1.
!
testPlatform
| platforms platform archs |
platforms := #( 'aix' 'darwin' 'freebsd' 'linux' 'openbsd' 'sunos' 'win32' 'android' ).
platform := Process platform.
self assert: [ platforms includes: platform ].
Process isWindows ifTrue: [ self assert: [ platform = 'win32' ] ].
Process isLinux ifTrue: [ self assert: [ platform = 'linux' ] ].
Process isMacos ifTrue: [ self assert: [ platform = 'darwin' ] ].
archs := #( 'arm' 'arm64' 'ia32' 'loong64' 'mips' 'mipsel' 'ppc64' 'riscv64' 's390' 's390x' 'x64' ).
self assert: [ archs includes: Process arch ].
!
testMemory
self assert: [ Process availableMemory > 1000 ].
self assert: [ Process constrainedMemory >= 0 ].
self assert: [ Process memoryUsage class = MemoryUsage ].
!
testIpc
self assert: [ Process connected isNil ].
"IPC not tested:"
"Process disconnect."
"Process channelRef."
"Process channelUnref."
!
testPids
self assert: [ Process pid > 0 ].
self assert: [ Process ppid > 0 ].
!
testUids
"Windows does not have these."
Process isWindows ifTrue: [ ^ self ].
"Setting UIDs and GIDs is not tested because it requires elevated rights."
self assert: [ Process uid >= 0 ].
self assert: [ Process euid >= 0 ].
self assert: [ Process gid >= 0 ].
self assert: [ Process egid >= 0 ].
!
testCpuUsage
self assert: [ Process cpuUsage class = CpuUsage ].
self assert: [ Process threadCpuUsage class = CpuUsage ].
!
testMisc
| nodeVersion |
"Only test this manually, to prevent polluting debug output."
"Process emitWarning: 'Careful, now...'."
self assert: [ Process hrtime > 1000 ].
self assert: [ Process uptime >= 0 ].
self assert: [ Process hasPermission: 'fs.read' reference: '.' ].
self assert: [ Process version startsWith: 'v' ].
nodeVersion := Process versions atJsProperty: 'node'.
self assert: [ nodeVersion = ( Process version slice: 1 ) ].
self assert: [ Process title length > 0 ].
!
testUmask
| oldUmask umask |
"On Windows, 'umask' always returns 0"
Process isWindows ifTrue: [ ^ self ].
"Process umask returns the pevious umask."
oldUmask := Process umask: 2.
umask := Process umask: oldUmask.
self assert: [ umask = 2 ].
!
CLASS TestProcessConfig EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| config variables targetDefaults |
config := Process config.
self assert: [ config class = ProcessConfig ].
variables := config targetDefaults.
self assert: [ ( variables atJsProperty: 'default_configuration' ) length > 0 ].
variables := config variables.
self assert: [ ( variables atJsProperty: 'host_arch' ) = Process arch ].
!
CLASS TestResourceUsage EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| resourceUsage |
resourceUsage := Process resourceUsage.
self assert: [ resourceUsage class = ResourceUsage ].
self assert: [ resourceUsage userCpuTime > 0 ].
self assert: [ resourceUsage systemCpuTime > 0 ].
self assert: [ resourceUsage maxRss > 0 ].
self assert: [ resourceUsage minorPageFault >= 0 ].
self assert: [ resourceUsage majorPageFault >= 0 ].
self assert: [ resourceUsage swappedOut >= 0 ].
self assert: [ resourceUsage fsRead >= 0 ].
self assert: [ resourceUsage fsWrite >= 0 ].
self assert: [ resourceUsage ipcSent >= 0 ].
self assert: [ resourceUsage ipcReceived >= 0 ].
self assert: [ resourceUsage signalsCount >= 0 ].
self assert: [ resourceUsage voluntaryContextSwitches >= 0 ].
self assert: [ resourceUsage involuntaryContextSwitches >= 0 ].
!
CLASS TestOs EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| loadavg tmpPath |
self assert: [ Os tmpDir length > 0 ].
tmpPath := Os tmpPath: 'smalljs-'.
self assert: [ tmpPath includes: Os tmpDir ].
self assert: [ tmpPath includes: 'smalljs-' ].
self assert: [ #( '\n' '\r\n' ) includes: Os eol ].
self assert: [ #( '/dev/null' '\\\\.\\nul' ) includes: Os devNull ].
"User"
self assert: [ Os homedir length > 0 ].
self assert: [ Os userInfo class = OsUserInfo ].
"Process"
self checkPriority.
self assert: [ Os uptime > 0 ].
loadavg := Os loadavg.
self assert: [ Os loadavg length = 3 ].
self assert: [ Os loadavg first >= 0.0 ].
self assert: [ Os freemem > 1000 ].
self assert: [ Os totalmem > 1000 ].
"OS"
self assert: [ #( 'Windows_NT' 'Linux' 'Darwin' ) includes: Os type ].
self assert: [ Os isWindows | Os isLinux | Os isMacos ].
self assert: [ Os platform length > 0 ].
self assert: [ Os version length > 0 ].
self assert: [ Os release length > 0 ].
"Host"
self assert: [ Os hostname length > 0 ].
self assert: [ Os machine length > 0 ].
self assert: [ Os networkInterfaces values first first class = OsNetworkInterfaceInfo ].
"CPU"
self assert: [ Os arch length > 0 ].
self assert: [ #( 'BE' 'LE' ) includes: Os endianness ].
self assert: [ Os availableParallelism > 0 ].
self assert: [ Os cpus first class = OsCpuInfo ].
!
checkPriority
| normalPriority lowerPriority |
normalPriority := OsConstants priority atJsProperty: 'PRIORITY_NORMAL'.
self assert: [ ( Os getPriority: 0 ) = normalPriority ].
"Linux and MacOS do not allow changing the process priority by default."
Os isLinux | Os isMacos ifTrue: [ ^ self ].
lowerPriority := OsConstants priority atJsProperty: 'PRIORITY_BELOW_NORMAL'.
Os setPriority: 0 to: lowerPriority.
self assert: [ ( Os getPriority: 0 ) = lowerPriority ].
Os setPriority: 0 to: normalPriority.
!
CLASS TestOsConstants EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| signals errno priority |
signals := OsConstants signals.
self assert: [ ( signals atJsProperty: 'SIGHUP' ) = 1 ].
errno := OsConstants errno.
self assert: [ ( errno atJsProperty: 'E2BIG' ) = 7 ].
priority := OsConstants priority.
self assert: [ ( priority atJsProperty: 'PRIORITY_NORMAL' ) = 0 ].
!
CLASS TestOsCpuInfo EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
Os cpus do: [ :cpuInfo |
self checkCpuInfo: cpuInfo ].
!
checkCpuInfo: cpuInfo
self assert: [ cpuInfo class = OsCpuInfo ].
self assert: [ cpuInfo model length > 0 ].
self assert: [ cpuInfo speed > 0 ].
self assert: [ cpuInfo user > 0 ].
self assert: [ cpuInfo sys > 0 ].
self assert: [ cpuInfo idle > 0 ].
self assert: [ cpuInfo irq >= 0 ].
self assert: [ cpuInfo nice >= 0 ].
!
CLASS TestOsNetworkInterfaceInfo EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| info |
Os networkInterfaces values do: [ :infos |
infos do: [ :info |
self checkInfo: info ] ].
!
checkInfo: info
self assert: [ info class = OsNetworkInterfaceInfo ].
info family = 'IPv4'
ifTrue: [ self checkInfoIpv4: info ]
ifFalse: [
info family = 'IPv6'
ifTrue: [ self checkInfoIpv6: info ]
ifFalse: [ self assert: [ false ] ] ].
!
checkInfoIpv4: info
self assert: [ ( info address search: '[0-9]+\\.' ) = 0 ].
self assert: [ ( info netmask search: '[0-9]+\\.' ) = 0 ].
self assert: [ ( info cidr search: '[0-9]+\\.' ) = 0 ].
self assert: [ ( info mac search: '[0-9a-z][0-9a-z]:' ) = 0 ].
self assert: [ info internal | true ].
!
checkInfoIpv6: info
self assert: [ ( info address search: '[0-9a-z]*:' ) = 0 ].
self assert: [ info scopeid >= 0 ].
self assert: [ ( info netmask search: '[0-9a-z]*:' ) = 0 ].
self assert: [ ( info cidr search: '[0-9a-z]*:' ) = 0 ].
self assert: [ ( info mac search: '[0-9a-z][0-9a-z]:' ) = 0 ].
self assert: [ info internal | true ].
!
CLASS TestOsUserInfo EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
METHODS
test
| info |
info := Os userInfo.
self assert: [ info class = OsUserInfo ].
self assert: [ info username length > 0 ].
self assert: [ info homedir length > 0 ].
Os isWindows
ifTrue: [ self checkInfoWindows: info ]
ifFalse: [ self checkInfoUnix: info ].
!
checkInfoWindows: info
self assert: [ info uid = -1 ].
self assert: [ info gid = -1 ].
self assert: [ info shell isNil ].
!
checkInfoUnix: info
self assert: [ info uid >= 0 ].
self assert: [ info gid >= 0 ].
self assert: [ info shell length > 0 ].
!
CLASS TestFileHandle EXTENDS Test MODULE TestNode CLASSVARS '' VARS 'fileName fileHandle'
"Also tests class: FileStats"
async test
await self open.
await self stat.
await self writeRead.
await self properties.
await self closeRemove.
!
async open
fileName := Os tmpPath: 'smalljs-tfh-'.
fileHandle := await FileHandle open: fileName flags: 'w+'.
self assert: [ fileHandle class = FileHandle ].
self assert: [ fileHandle jsClassName = 'FileHandle' ].
self assert: [ fileHandle fd >= 0 ].
!
async stat
| stats now |
stats := await fileHandle stat.
self assert: [ stats dev >= 1 ].
self assert: [ stats ino >= 1 ].
self assert: [ #( 33188 33204 33206 ) includes: stats mode ].
self assert: [ stats nlink = 1 ].
self assert: [ stats uid >= 0 ].
self assert: [ stats gid >= 0 ].
self assert: [ stats size = 0 ].
self assert: [ stats blksize >= 512 ].
now := Date now.
self assert: [ ( now - stats ctime toMilliseconds ) abs < 300000 ].
self assert: [ ( now - stats mtime toMilliseconds ) abs < 300000].
self assert: [ ( now - stats atime toMilliseconds ) abs < 300000 ].
self assert: [ ( now - stats ctimeMs ) abs < 300000 ].
self assert: [ ( now - stats mtimeMs ) abs < 300000 ].
self assert: [ ( now - stats atimeMs ) abs < 300000 ].
!
async writeRead
| writeBuffer bytesWritten readBuffer bytesRead buffer stats |
writeBuffer := Buffer new: 8.
0 to: 7 do: [ :index |
writeBuffer at: index put: 97 + index ].
bytesWritten := await fileHandle write: writeBuffer offset: 2 length: 4 position: nil.
self assert: [ bytesWritten = 4 ].
readBuffer := Buffer new: 8.
bytesRead := await fileHandle read: readBuffer offset: 2 length: 4 position: 0.
self assert: [ bytesRead = 4 ].
self assert: [ readBuffer length = 8 ].
self assert: [ ( readBuffer subarray: 2 to: 6 ) toArray = #( 99 100 101 102 ) ].
"Must reopen file to test writeFile."
await fileHandle close.
fileHandle := await FileHandle open: fileName flags: 'w+'.
buffer := Buffer from: 'abcd'.
await fileHandle writeFile: buffer.
"Must reopen file to test readFile."
await fileHandle close.
fileHandle := await FileHandle open: fileName flags: 'r+'.
buffer := await fileHandle readFile.
self assert: [ buffer toString = 'abcd' ].
await fileHandle truncate: 2.
stats := await fileHandle stat.
self assert: [ stats size = 2 ].
await fileHandle sync.
await fileHandle datasync.
!
async properties
| stats |
await fileHandle chmod: 33200.
stats := await fileHandle stat.
"On Windows chmod does work and stays 33206."
self assert: [ #( 33200 33206 ) includes: stats mode ].
"Don't actually change the user and group because we probably don't have permission.
Just set the currrent uid and gid and see if the call executes."
await fileHandle chown: stats uid gid: stats gid.
stats := await fileHandle stat.
self assert: [ stats uid >= 0 ].
self assert: [ stats gid >= 0 ].
await fileHandle atime: 1 mtime: 2.
stats := await fileHandle stat.
self assert: [ stats atimeMs = 1000 ].
self assert: [ stats mtimeMs = 2000 ].
!
async closeRemove
await fileHandle close.
Fs unlinkSync: fileName.
!
CLASS TestFs EXTENDS Test MODULE TestNode CLASSVARS ''
VARS 'tempDirName subDirName tempFilePath tempFileFd'
"Also tests classes Dir, Dirent in sync mode."
test
| prefix options mode |
"Create temp directory"
prefix := Path join: Os tmpDir with: 'smalljs-'.
tempDirName := Fs mkdtempSync: prefix options: nil.
self assert: [ tempDirName includes: 'smalljs-' ].
self assert: [ Fs existsSync: tempDirName ].
"Create subdirectory"
subDirName := Path join: tempDirName with: 'sub1/sub2'.
options := FileMkdirOptions new recursive: true.
Fs mkdirSync: subDirName options: options.
self assert: [ Fs existsSync: subDirName ].
"Create file"
tempFilePath := Path join: tempDirName with: 'tempfile.tmp'.
tempFileFd := Fs openSync: tempFilePath flags: 'w+' mode: 384.
self assert: [ tempFileFd >= 0 ].
Fs closeSync: tempFileFd.
mode := FileConstants fileOk.
Fs accessSync: tempFilePath mode: mode.
self writeReadFile.
!
writeReadFile
| writeBuffer readBuffer |
writeBuffer := Buffer from: 'abcd'.
Fs writeFileSync: tempFilePath data: writeBuffer options: nil.
writeBuffer := Buffer from: '12'.
Fs appendFileSync: tempFilePath data: writeBuffer options: nil.
readBuffer := Fs readFileSync: tempFilePath.
self assert: [ readBuffer toString = 'abcd12' ].
self writeReadBuffer.
!
writeReadBuffer
| writeBuffer readBuffer bytesRead |
tempFileFd := Fs openSync: tempFilePath flags: 'w+' mode: nil.
writeBuffer := Buffer from: 'efgh'.
Fs writeSync: tempFileFd buffer: writeBuffer options: nil.
Fs closeSync: tempFileFd.
tempFileFd := Fs openSync: tempFilePath flags: 'r+' mode: nil.
readBuffer := Buffer new: 8.
bytesRead := Fs readSync: tempFileFd buffer: readBuffer options: nil.
self assert: [ bytesRead = 4 ].
self assert: [ ( readBuffer subarray: 0 to: 4 ) toString = 'efgh' ].
Fs closeSync: tempFileFd.
self writeReadBufferPositioned.
!
writeReadBufferPositioned
| writeBuffer readBuffer bytesRead |
tempFileFd := Fs openSync: tempFilePath flags: 'w+' mode: nil.
writeBuffer := Buffer from: 'ijklmn'.
Fs writeSync: tempFileFd buffer: writeBuffer offset: 1 length: 4 position: nil.
Fs closeSync: tempFileFd.
tempFileFd := Fs openSync: tempFilePath flags: 'r+' mode: nil.
readBuffer := Buffer new: 8.
bytesRead := Fs readSync: tempFileFd buffer: readBuffer offset: 2 length: 2 position: 1.
self assert: [ bytesRead = 2 ].
self assert: [ ( readBuffer subarray: 2 to: 4 ) toString = 'kl' ].
Fs closeSync: tempFileFd.
self renameCopy.
!
renameCopy
| tempFilePath2 |
tempFilePath2 := Path join: tempDirName with: 'tempfile2.tmp'.
Fs renameSync: tempFilePath to: tempFilePath2.
self assert: [ ( Fs existsSync: tempFilePath ) not ].
self assert: [ Fs existsSync: tempFilePath2 ].
Fs copyFileSync: tempFilePath2 to: tempFilePath mode: nil.
self assert: [ Fs existsSync: tempFilePath ].
Fs unlinkSync: tempFilePath.
Fs cpSync: tempFilePath2 to: tempFilePath options: nil.
self assert: [ Fs existsSync: tempFilePath ].
Fs unlinkSync: tempFilePath2.
self accessPath.
!
accessPath
| stats |
Fs utimesSync: tempFilePath atime: 2 mtime: 1.
stats := Fs statSync: tempFilePath options: nil.
self assert: [ stats atimeMs = 2000 ].
self assert: [ stats mtimeMs = 1000 ].
"chmod and chown to nothing on Windows,
so don't actually change them and can't check results."
Fs chmodSync: tempFilePath mode: stats mode.
Fs chownSync: tempFilePath uid: stats uid gid: stats gid.
self accessFd
!
accessFd
| stats |
tempFileFd := Fs openSync: tempFilePath flags: 'r+' mode: nil.
Fs futimesSync: tempFileFd atime: 4 mtime: 3.
stats := Fs fstatSync: tempFileFd options: nil.
self assert: [ stats atimeMs = 4000 ].
self assert: [ stats mtimeMs = 3000 ].
"chmod and chown do nothing on Windows,
so don't actually change them and can't check results."
Fs fchmodSync: tempFileFd mode: stats mode.
Fs fchownSync: tempFileFd uid: stats uid gid: stats gid.
Fs closeSync: tempFileFd.
self truncate.
!
truncate
| stats |
Fs truncateSync: tempFilePath length: 2.
stats := Fs statSync: tempFilePath options: nil.
self assert: [ stats size = 2 ].
tempFileFd := Fs openSync: tempFilePath flags: 'r+' mode: nil.
Fs ftruncateSync: tempFileFd length: 0.
stats := Fs fstatSync: tempFileFd options: nil.
self assert: [ stats size = 0 ].
Fs fsyncSync: tempFileFd.
Fs fdatasyncSync: tempFileFd.
self directory.
!
directory
| pattern fileNames dir dirent |
pattern := Path join: tempDirName with: '**/**'.
fileNames := Fs globSync: pattern options: nil.
self assert: [ fileNames length = 4 ].
self assert: [ fileNames includes: tempDirName ].
fileNames := Fs readdirSync: tempDirName options: nil.
self assert: [ fileNames length = 2 ].
self assert: [ fileNames includes: 'sub1' ].
dir := Fs opendirSync: tempDirName options: nil.
fileNames := #().
[ ( dirent := dir readSync ) notNil ] whileTrue: [
self dirent: dirent.
fileNames add: dirent name ].
self assert: [ fileNames length = 2 ].
dir closeSync.
self end.
!
dirent: dirent
self assert: [ dirent parentPath = tempDirName ].
#( 'sub1' 'tempfile.tmp' ) includes: dirent name.
dirent name = 'tempfile.tmp' ifTrue: [
self assert: [ dirent isFile ] ].
dirent name = 'sub1' ifTrue: [
self assert: [ dirent isDirectory ] ].
self assert: [ dirent isCharacterDevice not ].
self assert: [ dirent isBlockDevice not ].
self assert: [ dirent isCharacterDevice not ].
self assert: [ dirent isSymbolicLink not ].
self assert: [ dirent isFifo not ].
self assert: [ dirent isSocket not ].
!
end
"Remove everything."
| options |
Fs unlinkSync: tempFilePath.
self assert: [ ( Fs existsSync: tempFilePath ) not ].
Fs rmdirSync: subDirName.
self assert: [ ( Fs existsSync: subDirName ) not ].
options := FileRmOptions new recursive: true.
Fs rmSync: tempDirName options: options.
self assert: [ ( Fs existsSync: tempDirName ) not ].
!
CLASS TestFsp EXTENDS Test MODULE TestNode CLASSVARS ''
VARS 'tempDirName subDirName tempFilePath'
"Also tests classes Dir, Dirent in async mode."
async test
await self makeTemp.
await self openClose.
await self renameCopyRemove.
await self properties.
await self directories.
await self remove.
!
async makeTemp
| prefix dirName options |
"Create temp directory"
prefix := Path join: Os tmpDir with: 'smalljs-tfsp-'.
tempDirName := await Fsp mkdtemp: prefix.
self assert: [ tempDirName includes: 'smalljs-' ].
self assert: [ Fs existsSync: tempDirName ].
"Create subdirectories"
subDirName := Path join: tempDirName with: 'sub1/sub2'.
options := FileMkdirOptions new recursive: true.
await Fsp mkdir: subDirName options: options.
self assert: [ Fs existsSync: subDirName ].
!
async openClose
| fileHandle mode |
tempFilePath := Path join: tempDirName with: 'tempfile.tmp'.
fileHandle := await Fsp open: tempFilePath flags: 'w+' mode: 384.
self assert: [ fileHandle fd >= 0 ].
await fileHandle close.
mode := FileConstants fileOk.
await Fsp access: tempFilePath mode: mode.
!
async writeRead
| buffer stats |
buffer := Buffer from: 'abcd'.
await Fsp writeFile: tempFilePath data: buffer options: nil.
buffer := Buffer from: '12'.
await Fsp appendFile: tempFilePath data: buffer options: nil.
buffer := await Fsp readFile: tempFilePath options: nil.
self assert: [ buffer toString = 'abcd12' ].
await Fsp truncate: tempFilePath length: 2.
stats := await Fsp stat: tempFilePath options: nil.
self assert: [ stats size = 2 ].
!
async renameCopyRemove
| tempFilePath2 |
tempFilePath2 := Path join: tempDirName with: 'tempfile2.tmp'.
await Fsp rename: tempFilePath to: tempFilePath2.
self assert: [ ( Fs existsSync: tempFilePath ) not ].
self assert: [ Fs existsSync: tempFilePath2 ].
await Fsp copyFile: tempFilePath2 to: tempFilePath mode: nil.
self assert: [ Fs existsSync: tempFilePath ].
await Fsp unlink: tempFilePath.
self assert: [ ( Fs existsSync: tempFilePath ) not ].
await Fsp cp: tempFilePath2 to: tempFilePath options: nil.
self assert: [ Fs existsSync: tempFilePath ].
await Fsp unlink: tempFilePath2.
self assert: [ ( Fs existsSync: tempFilePath2 ) not ].
!
async properties
| stats |
await Fsp utimes: tempFilePath atime: 2 mtime: 1.
stats := await Fsp stat: tempFilePath options: nil.
self assert: [ stats atimeMs = 2000 ].
self assert: [ stats mtimeMs = 1000 ].
"chmod and chown do nothing on Windows,
so don't actually change them and can't check for results."
await Fsp chmod: tempFilePath mode: stats mode.
await Fsp chown: tempFilePath uid: stats uid gid: stats gid.
!
async directories
| fileNames pattern dir dirent |
"Glob"
fileNames := #().
pattern := Path join: tempDirName with: '**/**'.
await Fsp glob: pattern options: nil
iterate: [ :fileName | fileNames add: fileName ].
self assert: [ fileNames length = 4 ].
self assert: [ fileNames includes: tempDirName ].
"Directory sync"
"Directory async"
fileNames := await Fsp readdir: tempDirName options: nil.
self assert: [ fileNames length = 2 ].
self assert: [ fileNames includes: 'sub1' ].
fileNames := #().
dir := await Fsp opendir: tempDirName options: nil.
await dir read: [ :dirent |
fileNames add: dirent name.
self checkDirent: dirent ].
self assert: [ fileNames length = 2 ].
await dir close.
!
checkDirent: dirent
self assert: [ dirent parentPath = tempDirName ].
#( 'sub1' 'tempfile.tmp' ) includes: dirent name.
dirent name = 'tempfile.tmp' ifTrue: [
self assert: [ dirent isFile ] ].
dirent name = 'sub1' ifTrue: [
self assert: [ dirent isDirectory ] ].
self assert: [ dirent isCharacterDevice not ].
self assert: [ dirent isBlockDevice not ].
self assert: [ dirent isCharacterDevice not ].
self assert: [ dirent isSymbolicLink not ].
self assert: [ dirent isFifo not ].
self assert: [ dirent isSocket not ].
!
async remove
| options |
await Fsp unlink: tempFilePath.
self assert: [ ( Fs existsSync: tempFilePath ) not ].
await Fsp rmdir: subDirName options: nil.
self assert: [ ( Fs existsSync: subDirName ) not ].
options := FileRmOptions new recursive: true.
await Fsp rm: tempDirName options: options.
self assert: [ ( Fs existsSync: tempDirName ) not ].
!
CLASS TestPath EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
Path isWindows
ifTrue: [ self checkWindows: Path ]
ifFalse: [ self checkPosix: Path ].
self checkWindows: Path win32.
self checkPosix: Path posix.
!
checkPosix: pathClass
| pathObject |
self assert: [ ( pathClass resolve: 'file.txt' ) endsWith: '/file.txt' ].
self assert: [ ( pathClass join: '/dir' with: 'file.txt' ) = '/dir/file.txt' ].
self assert: [ pathClass sep = '/' ].
self assert: [ pathClass delimiter = ':' ].
self assert: [ ( pathClass dirname: '/dir/file.txt' ) = '/dir' ].
self assert: [ ( pathClass basename: '/dir/file.txt' suffix: '' ) = 'file.txt' ].
self assert: [ ( pathClass basename: '/dir/file.txt' suffix: '.txt' ) = 'file' ].
self assert: [ ( pathClass extname: '/dir/file.txt' ) = '.txt' ].
pathObject := PathObject new
dir: '/dir'; name: 'file'; ext: '.txt'.
self assert: [ ( pathClass format: pathObject ) = '/dir/file.txt' ].
pathObject := pathClass parse: '/dir/file.txt'.
self assert: [ pathObject root = '/' ].
self assert: [ pathObject dir = '/dir' ].
self assert: [ pathObject base = 'file.txt' ].
self assert: [ pathObject name = 'file' ].
self assert: [ pathObject ext = '.txt' ].
self assert: [ pathClass matches: '/dir/file.txt' glob: '/dir/*' ].
self assert: [ pathClass isAbsolute: '/dir/file.txt' ].
self assert: [ pathClass isAbsolute: '/dir/file.txt' ].
self assert: [ ( pathClass isAbsolute: 'dir/file.txt' ) not ].
self assert: [ ( pathClass normalize: '/dir/../file.txt' ) = '/file.txt' ].
self assert: [ ( pathClass relativeFrom: '/a/b/c' to: '/a/b/d' ) = '../d' ].
self assert: [ ( pathClass toNamespacedPath: '/dir/file.txt' ) = '/dir/file.txt' ].
self assert: [ pathClass posix = pathClass win32 posix ].
self assert: [ pathClass win32 = pathClass posix win32 ].
!
checkWindows: pathClass
| pathObject |
self assert: [ ( pathClass resolve: 'file.txt' ) endsWith: '\\file.txt' ].
self assert: [ ( pathClass join: '\\dir' with: 'file.txt' ) = '\\dir\\file.txt' ].
self assert: [ pathClass sep = '\\' ].
self assert: [ pathClass delimiter = ';' ].
self assert: [ ( pathClass dirname: '\\dir\\file.txt' ) = '\\dir' ].
self assert: [ ( pathClass basename: '\\dir\\file.txt' suffix: '' ) = 'file.txt' ].
self assert: [ ( pathClass basename: '\\dir\\file.txt' suffix: '.txt' ) = 'file' ].
self assert: [ ( pathClass extname: '\\dir\\file.txt' ) = '.txt' ].
pathObject := PathObject new
dir: '\\dir'; name: 'file'; ext: '.txt'.
self assert: [ ( pathClass format: pathObject ) = '\\dir\\file.txt' ].
pathObject := pathClass parse: '\\dir\\file.txt'.
self assert: [ pathObject root = '\\' ].
self assert: [ pathObject dir = '\\dir' ].
self assert: [ pathObject base = 'file.txt' ].
self assert: [ pathObject name = 'file' ].
self assert: [ pathObject ext = '.txt' ].
self assert: [ pathClass matches: '\\dir\\file.txt' glob: '\\dir\\*' ].
self assert: [ pathClass isAbsolute: '\\dir\\file.txt' ].
self assert: [ pathClass isAbsolute: '\\dir\\file.txt' ].
self assert: [ ( pathClass isAbsolute: 'dir\\file.txt' ) not ].
self assert: [ ( pathClass normalize: '\\dir\\..\\file.txt' ) = '\\file.txt' ].
self assert: [ ( pathClass relativeFrom: '\\a\\b\\c' to: '\\a\\b\\d' ) = '..\\d' ].
"toNamespacedPath: only has Windows behavior when run on Windows OS,
so not when the pathClass win32 version is run on Linux."
Os isWindows ifTrue: [
self assert: [ ( pathClass toNamespacedPath: '\\dir\\file.txt' ) startsWith: '\\\\?' ] ].
self assert: [ pathClass posix = pathClass win32 posix ].
self assert: [ pathClass win32 = pathClass posix win32 ].
!
CLASS TestPathObject EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| pathObject |
pathObject := PathObject new.
self assert: [ pathObject class = PathObject ].
pathObject dir: '/dir'.
self assert: [ pathObject dir = '/dir' ].
pathObject root: '/'.
self assert: [ pathObject root = '/' ].
pathObject base: 'file.txt'.
self assert: [ pathObject base = 'file.txt' ].
pathObject name: 'file'.
self assert: [ pathObject name = 'file' ].
pathObject ext: '.txt'.
self assert: [ pathObject ext = '.txt' ].
!
CLASS TestFileRmOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options |
options := FileRmOptions new.
options force: true.
self assert: [ options force ].
options recursive: true.
self assert: [ options recursive ].
options retryDelay: 100.
self assert: [ options retryDelay = 100 ].
options maxRetries: 5.
self assert: [ options maxRetries = 5 ].
!
CLASS TestDirOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options |
options := DirOptions new.
options encoding: 'utf8'.
self assert: [ options encoding = 'utf8' ].
options withFileTypes: true.
self assert: [ options withFileTypes ].
options recursive: true.
self assert: [ options recursive ].
!
CLASS TestFileConstants EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
testAccess
self assert: [ FileConstants fileOk >= 0 ].
self assert: [ FileConstants readOk >= 0 ].
self assert: [ FileConstants writeOk >= 0 ].
self assert: [ FileConstants executeOk >= 0 ].
!
testCopy
self assert: [ FileConstants copyExclusive >= 0 ].
self assert: [ FileConstants copyLink >= 0 ].
self assert: [ FileConstants copyLinkForce >= 0 ].
!
CLASS TestFileCopyOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options |
options := FileCopyOptions new.
options dereference: true.
self assert: [ options dereference ].
options errorOnExist: true.
self assert: [ options errorOnExist ].
options force: true.
self assert: [ options force ].
options mode: 438.
self assert: [ options mode = 438 ].
options preserveTimestamps: true.
self assert: [ options preserveTimestamps ].
options recursive: true.
self assert: [ options recursive ].
options verbatimSymlinks: true.
self assert: [ options verbatimSymlinks ].
options filter: [ :src :dest | true ].
!
CLASS TestFileGlobOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options |
options := FileGlobOptions new.
options cwd: './web'.
self assert: [ options cwd = './web' ].
options withFileTypes: true.
self assert: [ options withFileTypes ].
options exclude: [ :path | false ].
!
CLASS TestFileMkdirOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options |
options := FileMkdirOptions new.
options recursive: true.
self assert: [ options recursive ].
options mode: 438.
self assert: [ options mode = 438 ].
!
CLASS TestFileOptions EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| options signal |
options := FileOptions new.
options encoding: 'utf8'.
self assert: [ options encoding = 'utf8' ].
options autoClose: true.
self assert: [ options autoClose ].
options emitClose: true.
self assert: [ options emitClose ].
options start: 10.
self assert: [ options start = 10 ].
options end: 70.
self assert: [ options end = 70 ].
options highWaterMark: 1024.
self assert: [ options highWaterMark = 1024 ].
signal := AbortSignal abort.
options signal: signal.
self assert: [ options signal = signal ].
!
CLASS MyType EXTENDS SqlObject MODULE TestDatabase CLASSVARS ''
VARS 'string integer float date boolean binary anil'
CLASSMETHODS
columns
^ #( #( 'string' String )
#( 'integer' Integer )
#( 'float' Float )
#( 'date' Date )
#( 'boolean' Boolean )
#( 'binary' Uint8Array )
#( 'anil' Nil ) ).
!
fromObject: object
^ self new
id: ( object atProperty: 'id' );
string: ( object atProperty: 'string' );
integer: ( object atProperty: 'integer');
float: ( object atProperty: 'float' );
date: ( object atProperty: 'date' );
boolean: ( object atProperty: 'boolean' );
binary: ( object atProperty: 'binary' );
anil: ( object atProperty: 'anil' ).
!
METHODS
"Accessing"
string
^ string.
!
string: aString
string := aString.
!
integer
^ integer.
!
integer: aInteger
integer := aInteger.
!
float
^ float.
!
float: aFloat
float := aFloat.
!
date
^ date.
!
date: aDate
date := aDate.
!
boolean
^ boolean.
!
boolean: aBoolean
boolean := aBoolean.
!
binary
^ binary.
!
binary: aBinary
binary := aBinary.
!
anil
^ anil.
!
anil: aAnil
anil := aAnil.
!
"Comparing"
= aType
^ ( id = aType id ) &
( string = aType string ) &
( integer = aType integer ) &
( ( float - aType float ) abs < 0.000001 ) &
( date = aType date ) &
( boolean = aType boolean ) &
( binary = aType binary ) &
( anil = aType anil ).
!
"Conversion"
toString
^ 'Type: id: ', string toString,
', string: ', string,
', integer: ', integer toString,
', float: ', float toString,
', date: ', date toString,
', boolean: ', boolean toString,
', binary: ', binary toString,
', anil: ', anil toString.
!
CLASS TestSqlDatabaseFactory EXTENDS Test MODULE TestDatabase CLASSVARS ''
VARS ''
test
self assert: [ ( SqlDatabaseFactory newFor: '../Database/SQLite/smalljs.db' ) class = SqliteDatabase ].
self assert: [ ( SqlDatabaseFactory newFor: 'postgres://postgres:postgres@localhost:5432/smalljs' ) class = PostgresDatabase ].
self assert: [ ( SqlDatabaseFactory newFor: 'mariadb://root:MariaDB@localhost:3307/smalljs?connectTimeout=0' ) class = MariadbDatabase ].
self assert: [ ( SqlDatabaseFactory newFor: 'mysql://root:MySQL@localhost:3306/smalljs?connectTimeout=0' ) class = MysqlDatabase ].
!
CLASS TestSqliteDatabaseSync EXTENDS Test MODULE TestDatabase CLASSVARS ''
VARS 'database typeTable type'
"Tests SQLite with its *sync* interface,
that is compatible with that of other supported databases with async interfaces when using await.
But awaits are not *necessary* and the testing functions don't need to be async.
Also tests SqliteTable."
disabled
"Disable these tests if database env var is not set."
^ self path isNil.
!
test
self open.
self deleteAll.
self tableInsert.
self databaseSelect.
self tableSelectAll.
self tableSelect.
self tableSelectParameters.
self tableSelectId.
self tableUpdate.
self tableDelete.
self close.
!
open
| path |
path := self path.
SqliteDatabase checkExists: path.
database := SqliteDatabase new: path.
database checkValid.
typeTable := database connectTable: 'Type' rowClass: MyType.
!
path
| path defaultPath |
path := Environment at: 'SMALLJS_SQLITE'.
path ifNotNil: [ ^ path ].
defaultPath := '../Database/SQLite/smalljs.db'.
( Fs existsSync: defaultPath ) ifTrue: [ ^ defaultPath ].
^ nil.
!
deleteAll
typeTable deleteAll.
!
tableInsert
"This test the default type member object for subsequent tests."
| binary |
binary := ( Uint8Array new: 8 ) fill: 240 start: 0 end: 8.
type := MyType new
string: 'Hello'; integer: 13; float: Float pi;
date: Date new; binary: binary; boolean: true.
typeTable insert: type.
self assert: [ type id > 0 ].
!
databaseSelect
| statement rows newType |
statement := database prepare: 'SELECT * FROM "Type" WHERE "string" = ?'.
rows := statement all: #( ( type string ) ).
self assert: [ rows length = 1 ].
newType := MyType fromObject: rows first.
"Manual type conversions, because the desired types are not known here."
newType date: ( Date fromString: newType date ).
newType boolean: ( Boolean fromInteger: newType boolean ).
self assert: [ newType = type ].
!
tableSelectAll
| selectedTypes |
selectedTypes := typeTable selectAll.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
tableSelect
| selectedTypes |
selectedTypes := typeTable select: '"string" = \'Hello\''.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
tableSelectParameters
| selectedTypes |
selectedTypes := typeTable select: '"integer" = ?' with: #( 13 ).
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
tableSelectId
| selectedType |
selectedType := typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
tableUpdate
| selectedType |
type string: 'World'.
typeTable update: type.
selectedType := typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
tableDelete
| selectedType |
typeTable delete: type.
selectedType := typeTable selectId: type id.
self assert: [ selectedType isNil ].
!
close
database close.
!
CLASS TestSqliteDatabaseOptions EXTENDS Test MODULE TestDatabase CLASSVARS '' VARS ''
test
| options |
options := SqliteDatabaseOptions new.
options open: true.
self assert: [ options open ].
options readOnly: true.
self assert: [ options readOnly ].
options enableForeignKeyConstraints: true.
self assert: [ options enableForeignKeyConstraints ].
options enableDoubleQuotedStringLiterals: true.
self assert: [ options enableDoubleQuotedStringLiterals ].
options allowExtension: true.
self assert: [ options allowExtension ].
!
CLASS TestPostgresDatabase EXTENDS Test MODULE TestDatabase CLASSVARS ''
VARS 'database typeTable type'
disabled
"Temporaryly disable this module when there are connection timeouts
due to breakpoints in unrelated async functions."
"^ true."
"Disable these tests if database env var is not set."
^ ( Environment at: 'SMALLJS_POSTGRES' ) isNil.
!
async test
await self connect.
await self tableDeleteAll.
await self tableInsert.
await self databaseSelect.
await self tableSelectAll.
await self tableSelect.
await self tableSelectWith.
await self tableSelectId.
await self tableUpdate.
await self tableDelete.
await self end.
!
async connect
| connectionString |
connectionString := Environment at: 'SMALLJS_POSTGRES'.
self assert: [ connectionString startsWith: 'postgres:' ].
database := PostgresDatabase new.
await database connect: connectionString.
typeTable := database connectTable: 'Type' rowClass: MyType.
!
async tableDeleteAll
await typeTable deleteAll.
!
async tableInsert
| binary |
binary := ( Uint8Array new: 6 ) fill: 127 start: 0 end: 6.
type := MyType new
string: 'Hi'; integer: 7; float: Float pi * 2;
date: Date new; binary: binary; boolean: true.
await typeTable insert: type.
self assert: [ type id > 0 ].
!
async databaseSelect
| query result selectedType |
query := 'SELECT * FROM "Type" WHERE "string" = $1'.
result := await database query: query with: #( ( type string ) ).
self assert: [ result rows length = 1 ].
selectedType := MyType fromObject: result rows first.
"Manual date type conversion, because the desired type is not known here."
selectedType date: ( Date fromString: selectedType date ).
self assert: [ selectedType = type ].
!
async tableSelectAll
| selectedTypes |
selectedTypes := await typeTable selectAll.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelect
| selectedTypes |
selectedTypes := await typeTable select: '`string` = "Hi"'.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectWith
| selectedTypes |
selectedTypes := await typeTable select: '`integer` = ?' with: #( 7 ).
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectId
| selectedType |
selectedType := await typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
async tableUpdate
| updatedType |
type string: 'There'.
await typeTable update: type.
updatedType := await typeTable selectId: type id.
self assert: [ updatedType notNil ].
self assert: [ updatedType = type ].
!
async tableDelete
| detetedType |
await typeTable delete: type.
detetedType := await typeTable selectId: type id.
self assert: [ detetedType isNil ].
!
async end
await database end.
!
CLASS TestMysqlDatabase EXTENDS Test MODULE TestDatabase CLASSVARS ''
VARS 'database typeTable type'
disabled
"Temporaryly disable this module when there are connection timeouts
due to breakpoints in unrelated async functions."
"^ true."
"Disable these tests if database env var is not set."
^ ( Environment at: 'SMALLJS_MYSQL' ) isNil.
!
async test
await self connect.
await self tableDeleteAll.
await self tableInsert.
await self databaseSelect.
await self tableSelectAll.
await self tableSelect.
await self tableSelectWith.
await self tableSelectId.
await self tableUpdate.
await self tableDelete.
await self end.
!
async connect
| connectionString |
connectionString := Environment at: 'SMALLJS_MYSQL'.
self assert: [ connectionString startsWith: 'mysql:' ].
database := MysqlDatabase new.
await database connect: connectionString.
typeTable := database connectTable: 'type' rowClass: MyType.
!
async tableDeleteAll
await typeTable deleteAll.
!
async tableInsert
| binary |
"The mysql2 package supports Node.js class Buffer and not the common class UInt8Array."
binary := ( Buffer new: 6 ) fill: 127 start: 0 end: 6.
type := MyType new
string: 'Hi'; integer: 7; float: Float pi * 2;
date: Date new; binary: binary; boolean: true.
await typeTable insert: type.
self assert: [ type id > 0 ].
!
async databaseSelect
| query result selectedType |
query := 'SELECT * FROM `Type` WHERE `string` = ?'.
result := await database query: query with: #( ( type string ) ).
self assert: [ result length = 1 ].
selectedType := MyType fromObject: result first.
"Manual date and boolean type conversion."
selectedType date: ( Date fromString: selectedType date ).
selectedType boolean: ( Boolean fromInteger: selectedType boolean ).
self assert: [ selectedType = type ].
!
async tableSelectAll
| selectedTypes |
selectedTypes := await typeTable selectAll.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelect
| selectedTypes |
selectedTypes := await typeTable select: '`string` = "Hi"'.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectWith
| selectedTypes |
selectedTypes := await typeTable select: '`integer` = ?' with: #( 7 ).
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectId
| selectedType |
selectedType := await typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
async tableUpdate
| selectedType |
type string: 'There'.
await typeTable update: type.
selectedType := await typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
async tableDelete
| selectedType |
await typeTable delete: type.
selectedType := await typeTable selectId: type id.
self assert: [ selectedType isNil ].
!
async end
await database end.
!
CLASS TestMariadbDatabase EXTENDS Test MODULE TestDatabase CLASSVARS ''
VARS 'database typeTable type'
"Also tests class MariadbTable"
disabled
"Temporaryly disable this module when there are connection timeouts
due to breakpoints in unrelated async functions."
"^ true."
"Disable these tests if database env var is not set."
^ ( Environment at: 'SMALLJS_MARIADB' ) isNil.
!
async test
await self connect.
await self tableDeleteAll.
await self tableInsert.
await self databaseSelect.
await self tableSelectAll.
await self tableSelect.
await self tableSelectWith.
await self tableSelectId.
await self tableUpdate.
await self tableDelete.
await self end.
!
async connect
| connectionString |
connectionString := Environment at: 'SMALLJS_MARIADB'.
self assert: [ connectionString startsWith: 'mariadb:' ].
database := MariadbDatabase new.
await database connect: connectionString.
typeTable := database connectTable: 'Type' rowClass: MyType.
!
async tableDeleteAll
await typeTable deleteAll.
!
async tableInsert
| binary |
"The mariadb npm package supports Node.js class Buffer and not the common class UInt8Array."
binary := ( Buffer new: 6 ) fill: 127 start: 0 end: 6.
type := MyType new
string: 'Hi'; integer: 7; float: Float pi * 2;
date: Date new; binary: binary; boolean: true.
await typeTable insert: type.
self assert: [ type id > 0 ].
!
async databaseSelect
| result newType |
result := await database query: 'SELECT * FROM `Type` WHERE `string` = ?' with: #( ( type string ) ).
self assert: [ result length = 1 ].
newType := MyType fromObject: result first.
"Manual type conversion, because the desired type is not known here."
newType date: ( Date fromString: newType date ).
newType boolean: ( Boolean fromInteger: newType boolean ).
self assert: [ newType = type ].
!
async tableSelectAll
| selectedTypes |
selectedTypes := await typeTable selectAll.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelect
| selectedTypes |
selectedTypes := await typeTable select: '`string` = "Hi"'.
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectWith
| selectedTypes |
selectedTypes := await typeTable select: '`integer` = ?' with: #( 7 ).
self assert: [ selectedTypes length = 1 ].
self assert: [ selectedTypes first = type ].
!
async tableSelectId
| selectedType |
selectedType := await typeTable selectId: type id.
self assert: [ selectedType notNil ].
self assert: [ selectedType = type ].
!
async tableUpdate
| updatedType |
type string: 'There'.
await typeTable update: type.
updatedType := await typeTable selectId: type id.
self assert: [ updatedType notNil ].
self assert: [ updatedType = type ].
!
async tableDelete
| deletedType |
await typeTable delete: type.
deletedType := await typeTable selectId: type id.
self assert: [ deletedType isNil ].
!
async end
await database end.
!
CLASS TestBuffer EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
testCreation
| buffer |
self assert: [ Buffer new length = 0 ].
buffer := Buffer new: 3.
self assert: [ ( Buffer new: 3 ) length = 3 ].
self assert: [ ( Buffer new: 3 ) last = 0 ].
self assert: [ ( Buffer with: 7 ) first = 7 ].
self assert: [ ( Buffer with: 8 with: 9 ) last = 9 ].
buffer := Buffer from: 'abc'.
self assert: [ ( buffer at: 1 ) = 98 ].
!
CLASS TestEventEmitter EXTENDS Test MODULE TestNode CLASSVARS '' VARS ''
test
| eventEmitter listeners |
eventEmitter := EventEmitter new.
self assert: [ eventEmitter jsClassName = 'EventEmitter' ].
"Max"
eventEmitter maxListeners: 20.
self assert: [ eventEmitter maxListeners = 20 ].
"Add"
eventEmitter on: 'test' then: [ self onTest ].
eventEmitter emit: 'test'.
self assert: [ ( eventEmitter listenerCount: 'test' ) = 1 ].
listeners := eventEmitter listeners: 'test'.
self assert: [ listeners length = 1 ].
self assert: [ listeners first class = Block ].
eventEmitter on: 'testArg' class: Integer
then: [ :arg | self onTestArg: arg ].
eventEmitter emit: 'testArg' value: 23.
self assert: [ ( eventEmitter listenerCount: 'testArg' ) = 1 ].
self assert: [ eventEmitter eventNames = #( 'test' 'testArg') ].
"Remove"
eventEmitter removeAllListeners: 'test'.
self assert: [ ( eventEmitter listenerCount: 'test' ) = 0 ].
"Once"
eventEmitter once: 'testOnce' then: [ self onTestOnce ].
self assert: [ ( eventEmitter listenerCount: 'testOnce' ) = 1 ].
eventEmitter emit: 'testOnce'.
self assert: [ ( eventEmitter listenerCount: 'testOnce' ) = 0 ].
eventEmitter once: 'testOnceArg' class: String
then: [ :arg | self onTestOnceArg: arg ].
self assert: [ ( eventEmitter listenerCount: 'testOnceArg' ) = 1 ].
eventEmitter emit: 'testOnceArg' value: 'arg'.
self assert: [ ( eventEmitter listenerCount: 'testOnceArg' ) = 0 ].
!
onTest
self assert: [ true ].
!
onTestArg: arg
self assert: [ arg = 23 ].
!
onTestOnce
self assert: [ true ].
!
onTestOnceArg: arg
self assert: [ arg = 'arg' ].
!
CLASS TestBrowserWindowOptions EXTENDS Test MODULE TestElectronMain CLASSVARS '' VARS ''
test
| options webPreferences |
options := BrowserWindowOptions new.
options width: 1000.
self assert: [ options width = 1000 ].
options height: 800.
self assert: [ options height = 800 ].
webPreferences := WebPreferences new.
options webPreferences: webPreferences.
self assert: [ options webPreferences = webPreferences ].
!
CLASS TestWebPreferences EXTENDS Test MODULE TestElectronMain CLASSVARS '' VARS ''
test
| webPreferences |
webPreferences := WebPreferences new.
webPreferences nodeIntegration: true.
self assert: [ webPreferences nodeIntegration ].
webPreferences nodeIntegrationInWorker: true.
self assert: [ webPreferences nodeIntegrationInWorker ].
webPreferences nodeIntegrationInSubFrames: true.
self assert: [ webPreferences nodeIntegrationInSubFrames ].
webPreferences contextIsolation: true.
self assert: [ webPreferences contextIsolation ].
webPreferences contextIsolation: true.
self assert: [ webPreferences contextIsolation ].
webPreferences sandbox: false.
self assert: [ webPreferences sandbox not ].
webPreferences preload: ( Path resolve: 'preload.mjs' ).
self assert: [ webPreferences preload endsWith: 'preload.mjs' ].
!
CLASS TestBlob EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| blob |
blob := Blob fromArray: ( Uint8Array from: #( 4 5 6 7 ) ).
self assert: [ blob size = 4 ].
self assert: [ blob type = '' ].
self assert: [ ( blob slice: 1 to: 3 ) size = 2 ].
!
testStream
| blob stream |
blob := Blob fromArray: ( Uint8Array from: #( 4 5 6 7 ) ).
self assert: [ blob size = 4 ].
self assert: [ blob type = '' ].
self assert: [ blob stream class = ReadableStream ].
blob arrayBufferThen: [ :arrayBuffer | self onArrayBuffer: arrayBuffer ].
blob textThen: [ :string | self onText: string ]
!
onText: string
self assert: [ string = '4567' ].
!
onArrayBuffer: arrayBuffer
self assert: [ arrayBuffer byteLength = 4 ].
self assert: [ ( ArrayBuffer isView: arrayBuffer ) not ].
!
CLASS TestFile EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
"TODO: Implement more tests."
CLASS TestReadableStream EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| stream streamTee |
stream := ( Blob fromArray: ( Uint8Array from: #( 4 5 6 7 ) ) ) stream.
self assert: [ stream class = ReadableStream ].
self assert: [ stream locked not ].
self assert: [ stream getReader class = ReadableStreamDefaultReader ].
self assert: [ stream locked ].
stream := ( Blob fromArray: ( Uint8Array from: #( 4 5 6 7 ) ) ) stream.
streamTee := stream tee.
self assert: [ streamTee first class = ReadableStream ].
streamTee first cancelThen: [ self onCancel ].
!
onCancel
self assert: [ true ].
!
"TODO:
- pipeThrough: transformStream options: options
- pipeTo: writeableStream options: options
- tee"
CLASS TestAbstractInteger EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ 42 toInteger = 42 ].
!
testIteration
| sum |
sum := 0.
1 to: 4 do: [ :num | sum := sum + num ].
self assert: [ sum = 10 ].
sum := 0.
4 to: 1 by: -1 do: [ :num | sum := sum + num ].
self assert: [ sum = 10 ].
sum := 0.
3 timesRepeat: [ sum increment ].
self assert: [ sum = 3 ].
!
async testIterationAwait
| sum |
sum := 0.
await 1 to: 4 doAwait: async [ :num |
await Timer timeout: 1.
sum := sum + num ].
self assert: [ sum = 10 ].
sum := 0.
await 4 to: 1 by: -1 doAwait: async [ :num |
await Timer timeout: 1.
sum := await sum + num ].
self assert: [ sum = 10 ].
sum := 0.
await 3 timesRepeatAwait: async [
await Timer timeout: 1.
sum := sum + 1 ].
self assert: [ sum = 3 ].
!
testFunctions
self assert: [ 8 even ].
self assert: [ 7 odd ].
self assert: [ 4 factorial = 24 ].
self assert: [ 23 isPrime ].
self assert: [ 21 isPrime not ].
self assert: [ 21 factors last = 7 ].
self assert: [ 21 firstDivider = 3 ].
!
CLASS TestBigInt EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ 9007199254740992 class = BigInt ].
self assert: [ 13 toBigInt class = BigInt ].
!
testConversion
self assert: [ 13 toBigInt toString = '13' ].
self assert: [ 13 toBigInt toFloat = 13.0 ].
!
testComparisons
self assert: [ 11 toBigInt = 11 toBigInt ].
self assert: [ 11 toBigInt <= 11 toBigInt ].
self assert: [ 11 toBigInt >= 11 toBigInt ].
self assert: [ 11 toBigInt < 13 toBigInt ].
self assert: [ 13 toBigInt > 11 toBigInt ].
"Comparisons with integer arguments."
self assert: [ 11 toBigInt = 11 ].
self assert: [ 11 toBigInt <= 11 ].
self assert: [ 11 toBigInt >= 11 ].
self assert: [ 11 toBigInt < 12 ].
self assert: [ 13 toBigInt > 12 ].
"Comparisons with float arguments."
self assert: [ 11 toBigInt = 11.0 ].
self assert: [ 11 toBigInt <= 11.0 ].
self assert: [ 11 toBigInt >= 11.0 ].
self assert: [ 11 toBigInt < 12.0 ].
self assert: [ 13 toBigInt > 12.0 ].
"Comparions with fractions."
self assert: [ 11 toBigInt = ( 22 / 2 ) ].
self assert: [ 11 toBigInt <= ( 23 / 2 ) ].
self assert: [ 11 toBigInt >= ( 21 / 2 ) ].
self assert: [ 11 toBigInt < ( 23 / 2 ) ].
self assert: [ 13 toBigInt > ( 25 / 2 ) ].
!
testBasicArithmatic
"Basic arithmatic with long integer arguments."
self assert: [ 3 toBigInt + 4 toBigInt = 7 toBigInt ].
self assert: [ 4 toBigInt - 3 toBigInt = 1 toBigInt ].
self assert: [ 3 toBigInt * 4 toBigInt = 12 toBigInt ].
self assert: [ 4 toBigInt / 3 toBigInt = ( 4 / 3 ) ].
self assert: [ 7 toBigInt // 3 toBigInt = 2 toBigInt ].
self assert: [ 7 toBigInt % 3 toBigInt = 1 toBigInt ].
self assert: [ 7 toBigInt ** 3 toBigInt = 343 toBigInt ].
"Basic arithmatic with integer arguments."
self assert: [ 3 toBigInt + 10 = 13 ].
self assert: [ 3 toBigInt - 5 = -2 ].
self assert: [ 3 toBigInt * 2 = 6 ].
self assert: [ 3 toBigInt / 4 = ( 3 / 4 ) ].
self assert: [ 7 toBigInt // 3 = 2 ].
self assert: [ 7 toBigInt % 3 = 1 ].
self assert: [ 7 toBigInt ** 3 = 343 toBigInt ].
"Basic arithmatic with fraction arguments."
self assert: [ 3 toBigInt + ( 1 / 2 ) = ( 7 / 2 ) ].
self assert: [ 4 toBigInt - ( 1 / 2 ) = ( 7 / 2 ) ].
self assert: [ 4 toBigInt * ( 1 / 2 ) = 2 ].
self assert: [ 2 toBigInt / ( 1 / 2 ) = 4 ].
self assert: [ 3 toBigInt // ( 2 / 3 ) = 4 ].
self assert: [ 2 toBigInt % ( 4 / 3 ) = ( 2 / 3 ) ].
self assert: [ 2 toBigInt ** ( 1 / 2 ) equals: 2 sqrt precision: 0.001 ].
"Basic arithmatic with float arguments."
self assert: [ 3 toBigInt + 10.0 = 13.0 ].
self assert: [ 3 toBigInt - 5.0 = -2.0 ].
self assert: [ 3 toBigInt * 2.0 = 6.0 ].
self assert: [ 3 toBigInt / 4.0 = 0.75 ].
self assert: [ 7 toBigInt // 3.0 = 2.0 ].
self assert: [ 7 toBigInt % 3.0 = 1.0 ].
self assert: [ 2 toBigInt ** 0.5 equals: 2 sqrt precision: 0.001 ].
"Basic arithmatic with points."
self assert: [ 3 toBigInt + ( 1 @ 2 ) = ( 4 @ 5 ) ].
self assert: [ 4 toBigInt - ( 1 @ 2 ) = ( 3 @ 2 ) ].
self assert: [ 2 toBigInt * ( 2 @ 3 ) = ( 4 @ 6 ) ].
self assert: [ 6 toBigInt / ( 2 @ 3 ) = ( 3 @ 2 ) ].
self assert: [ 7 toBigInt // ( 2 @ 3 ) = ( 3 @ 2 ) ].
self assert: [ 8 toBigInt % ( 2 @ 3 ) = ( 0 @ 2 ) ].
!
testBitOperations
self assert: [ 6 toBigInt & 3 toBigInt = 2 ].
self assert: [ 6 toBigInt | 3 toBigInt = 7 ].
self assert: [ ( 6 toBigInt xor: 3 toBigInt ) = 5 ].
self assert: [ 7 toBigInt >> 1 toBigInt = 3 ].
self assert: [ 3 toBigInt << 1 toBigInt = 6 ].
!
testGcd
self assert: [ ( 12 toBigInt gcd: 9 toBigInt ) = 3 ].
self assert: [ ( 12 toBigInt gcd: 9 ) = 3 ].
!
CLASS TestCharacter EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ ( Character fromCode: 65 ) = $A ].
self assert: [ Character newline code = 10 ].
!
testAcesssing
self assert: [ $A code = 65 ].
!
testConverting
self assert: [ $a toString = 'a' ].
self assert: [ $a toUpperCase = $A ].
self assert: [ $1 toUpperCase = $1 ].
self assert: [ $B toLowerCase = $b ].
self assert: [ $@ toUpperCase = $@ ].
!
testComparisons
self assert: [ $a = $a ].
self assert: [ $a ~= $b ].
self assert: [ $a < $b ].
self assert: [ $a <= $a ].
!
testTesting
self assert: [ $a isLowerCase ].
self assert: [ $Z isUpperCase ].
self assert: [ $e isLetter ].
self assert: [ $. isLetter not ].
!
CLASS TestDate EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| msec date1 date1Utc date2 now |
msec := Date now.
self assert: [ Date now > 1730754619592 ].
date1 := Date year: 2000 month: 2 day: 28 hours: 23 minutes: 58 seconds: 59.
self assert: [ ( date1 year = 2000 ) & ( date1 month = 2 ) & ( date1 day = 28 ) ].
self assert: [ ( date1 hours = 23 ) & ( date1 minutes = 58 ) & ( date1 seconds = 59 ) ].
date2 := date1 copy.
self assert: [ date1 = date2 ].
date2 year: 2001.
self assert: [ date1 ~= date2 ].
date2 := Date new year: 1996; month: 10; day: 30; hours: 22; minutes: 21; seconds: 20; milliseconds: 123.
self assert: [ ( date2 year = 1996 ) & ( date2 month = 10 ) & ( date2 day = 30 ) ].
self assert: [ ( date2 hours = 22 ) & ( date2 minutes = 21 ) & ( date2 seconds = 20 ) & ( date2 milliseconds = 123 ) ].
date2 utcYear: 1995; utcMonth: 9; utcDay: 15; utcHours: 14; utcMinutes: 13; utcSeconds: 12; utcMilliseconds: 999.
self assert: [ ( date2 utcYear = 1995 ) & ( date2 utcMonth = 9 ) & ( date2 utcDay = 15 ) ].
self assert: [ ( date2 utcHours = 14 ) & ( date2 utcMinutes = 13 ) & ( date2 utcSeconds = 12 ) & ( date2 utcMilliseconds = 999 ) ].
self assert: [ ( date1 = date2 ) not ].
self assert: [ ( Date fromMilliseconds: date1 toMilliseconds ) = date1 ].
self assert: [ ( Date fromSeconds: date1 toSeconds ) = date1 ].
self assert: [ date1 = date1 ].
self assert: [ date2 < date1 ].
self assert: [ date2 <= date1 ].
self assert: [ date1 > date2 ].
self assert: [ date1 < Date new ].
date1Utc := Date utcYear: 2000 month: 2 day: 28 hours: 23 minutes: 58 seconds: 59.
self assert: [ date1 toMilliseconds - ( date1 timezoneOffset * 60 * 1000 ) = date1Utc toMilliseconds ].
now := Date new.
self assert: [ ( Date fromString: now toIsoString ) = now ].
self assert: [ date2 toDateString = 'Sun Oct 15 1995' ].
self assert: [ date2 toTimeString includes: ':12' ].
self assert: [ date2 toUtcString = 'Sun, 15 Oct 1995 14:13:12 GMT' ].
self assert: [ date2 toIsoString = '1995-10-15T14:13:12.999Z' ].
self assert: [ date2 toJson = '1995-10-15T14:13:12.999Z' ].
self assert: [ date2 toLocaleDateString includes: '15' ].
self assert: [ date2 toLocaleTimeString includes: '13' ].
self assert: [ Date isJsDate: INLINE 'new Date()' ].
self assert: [ ( Date isJsDate: INLINE '1' ) not ].
!
CLASS TestFloat EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testConstants
self assert: [ Float pi toString startsWith: '3.14' ].
self assert: [ Float epsilon < 0.000000001 ].
!
testConversion
self assert: [ 3.4 toInteger = 3 ].
self assert: [ 4.0 toString = '4' ].
self assert: [ 2.5 f16Round = 2.5 ].
"2.6 rounded to float16 is about: 2.599609375"
self assert: [ 2.6 f16Round - 2.6 < -0.00001 ].
self assert: [ 9007199254740991.0 isSafeInteger ].
self assert: [ 9007199254740992.0 isSafeInteger not ].
!
testComparisons
| bigInt fraction |
self assert: [ 3.0 = 3.0 ].
self assert: [ 7.0 <= 7.0 ].
self assert: [ 7.0 >= 7.0 ].
self assert: [ 2.9999999 equals: 3.0 precision: 0.00001 ].
self assert: [ 2.9999999 < 3.0 ].
self assert: [ 3.01 > 3.0 ].
"Compare to integers."
self assert: [ 3.0 = 3 ].
self assert: [ 3.0 <= 3 ].
self assert: [ 3.0 >= 3 ].
self assert: [ 2.9 < 3 ].
self assert: [ 3.1 > 3 ].
"Compare to large integers."
bigInt := BigInt fromJs: INLINE '13n'.
self assert: [ bigInt class = BigInt ].
self assert: [ 13.0 = bigInt ].
self assert: [ 13.0 <= bigInt ].
self assert: [ 13.0 >= bigInt ].
self assert: [ 12.9 < bigInt ].
self assert: [ 13.1 > bigInt ].
"Compare to fractions."
fraction := 1 / 2.
self assert: [ fraction class = Fraction ].
self assert: [ 0.5 = fraction ].
self assert: [ 0.5 <= fraction ].
self assert: [ 0.5 >= fraction ].
self assert: [ 0.499 < fraction ].
self assert: [ 0.501 > fraction ].
!
testBasicMath
| epsilon bigInt fraction point |
epsilon := 0.00001.
"Basic math with floats."
self assert: [ 2.0 + 3.0 = 5.0 ].
self assert: [ 2.1 - 3.0 = -0.9 ].
self assert: [ 4.1 * 3.0 equals: 12.3 precision: epsilon ].
self assert: [ 7.0 / 2.0 = 3.5 ].
self assert: [ 7.0 // 2.0 = 3.0 ].
self assert: [ 7.0 % 2.0 = 1.0 ].
self assert: [ 7.0 ** 2.0 equals: 49.0 precision: epsilon ].
"Basic math with integers."
self assert: [ 2.0 + 3 = 5.0 ].
self assert: [ 2.1 - 3 = -0.9 ].
self assert: [ 4.1 * 3 equals: 12.3 precision: epsilon ].
self assert: [ 7.0 / 2 = 3.5 ].
self assert: [ 7.0 // 2 = 3.0 ].
self assert: [ 7.0 % 2 = 1.0 ].
self assert: [ 7.0 ** 2 equals: 49.0 precision: epsilon ].
"Basic math with large integers."
bigInt := BigInt fromJs: INLINE '3n'.
self assert: [ bigInt class = BigInt ].
self assert: [ 2.0 + bigInt = 5.0 ].
self assert: [ 2.1 - bigInt = -0.9 ].
self assert: [ 4.1 * bigInt -12.3 < epsilon ].
bigInt := BigInt fromJs: INLINE '2n'.
self assert: [ 7.0 / bigInt = 3.5 ].
self assert: [ 7.0 // bigInt = 3.0 ].
self assert: [ 7.0 % bigInt = 1.0 ].
self assert: [ 7.0 ** bigInt equals: 49.0 precision: epsilon ].
"Basic math with fractions."
fraction := 1 / 2.
self assert: [ fraction class = Fraction ].
self assert: [ 1.0 + fraction = 1.5 ].
self assert: [ -1.0 - fraction = -1.5 ].
self assert: [ 4.2 * fraction = 2.1 ].
self assert: [ 7.0 / fraction = 14.0 ].
self assert: [ 7.1 // fraction = 14.0 ].
self assert: [ 7.1 % fraction equals: 0.1 precision: epsilon ].
self assert: [ 2.0 ** fraction equals: 2.0 sqrt precision: epsilon ].
"Basic math with points."
point := 2.5 @ 3.0.
self assert: [ point class = Point ].
self assert: [ 1.0 + point = ( 3.5 @ 4.0 ) ].
self assert: [ -1.0 - point = ( -3.5 @ -4.0 ) ].
self assert: [ 2.0 * point = ( 5.0 @ 6.0 ) ].
self assert: [ 30.0 / point = ( 12.0 @ 10.0 ) ].
self assert: [ 5.0 // point = ( 2.0 @ 1.0 ) ].
self assert: [ 5.0 % point = ( 0.0 @ 2.0 ) ].
!
testFunctions
self assert: [ 2.0 sqrt equals: 1.4142135623730951 precision: 0.00001 ].
self assert: [ 2.0 ln equals: 0.6931471805599453 precision: 0.00001 ].
self assert: [ 2.0 log equals: 0.30102999566398114 precision: 0.00001 ].
self assert: [ 2.0 exp equals: 7.38905609893065 precision: 0.00001 ].
self assert: [ 2.0 ** 3 = 8.0 ].
self assert: [ Float pi sin equals: 0.0 precision: 0.00001 ].
self assert: [ Float pi cos equals: -1.0 precision: 0.00001 ].
self assert: [ Float pi tan equals: 0.0 precision: 0.00001 ].
!
CLASS TestFraction EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ ( Fraction numerator: 2 denominator: 3 ) = ( 2 / 3 ) ].
self assert: [ ( Fraction numerator: 4 denominator: 6 ) = ( 2 / 3 ) ].
!
testConversion
self assert: [ ( 5 / 3 ) toInteger = 1 ].
self assert: [ ( 3 / 4 ) toFloat = 0.75 ].
self assert: [ ( 3 / 4 ) toString = '( 3 / 4 )' ].
!
testAccessing
self assert: [ ( Fraction numerator: 2 denominator: 3 ) numerator = 2 ].
self assert: [ ( Fraction numerator: 2 denominator: 3 ) denominator = 3 ].
!
testComparisons
| bigInt fraction |
self assert: [ ( 2 / 3 ) = ( 4 / 6 ) ].
self assert: [ ( 2 / 3 ) <= ( 2 / 3 ) ].
self assert: [ ( 2 / 3 ) >= ( 2 / 3 ) ].
self assert: [ ( 2 / 3 ) < ( 5 / 6 ) ].
self assert: [ ( 2 / 3 ) > ( 3 / 6 ) ].
"Compare to integers."
self assert: [ ( 4 / 2 ) = 2 ].
self assert: [ ( 4 / 2 ) <= 2 ].
self assert: [ ( 4 / 2 ) >= 2 ].
self assert: [ ( 7 / 3 ) < 3 ].
self assert: [ ( 7 / 3 ) > 2 ].
"Compare to large integers."
bigInt := BigInt fromJs: INLINE '2n'.
self assert: [ ( 4 / 2 ) = bigInt ].
self assert: [ ( 4 / 2 ) <= bigInt ].
self assert: [ ( 4 / 2 ) >= bigInt ].
self assert: [ ( 5 / 3 ) < bigInt ].
self assert: [ ( 7 / 3 ) > bigInt ].
!
testBasicMath
| bigInt point |
"Basic math with fractions."
self assert: [ ( 1 / 3 ) + ( 1 / 6 ) = ( 1 / 2 ) ].
self assert: [ ( 1 / 3 ) - ( 1 / 6 ) = ( 1 / 6 ) ].
self assert: [ ( 1 / 3 ) * ( 2 / 3 ) = ( 2 / 9 ) ].
self assert: [ ( 1 / 3 ) / ( 2 / 3 ) = ( 1 / 2 ) ].
self assert: [ ( 7 / 3 ) // ( 1 / 2 ) = 4 ].
self assert: [ ( 7 / 3 ) % ( 1 / 2 ) = ( 1 / 3 ) ].
self assert: [ ( 7 / 3 ) % ( 1 / 2 ) = ( 1 / 3 ) ].
self assert: [ ( 1 / 2 ) ** ( 1 / 2 ) equals: 0.707 precision: 0.01 ].
"Basic math with integers."
self assert: [ ( 2 / 3 ) + 2 = ( 8 / 3 ) ].
self assert: [ ( 4 / 3 ) - 1 = ( 1 / 3 ) ].
self assert: [ ( 4 / 3 ) * 2 = ( 8 / 3 ) ].
self assert: [ ( 4 / 3 ) / 2 = ( 2 / 3 ) ].
self assert: [ ( 8 / 3 ) // 2 = 1 ].
self assert: [ ( 8 / 3 ) % 2 = ( 2 / 3 ) ].
self assert: [ ( 1 / 2 ) ** 2 equals: 0.25 precision: 0.01 ].
"Basic math with points."
self assert: [ ( 1 / 3 ) + ( 2 @ 3 ) = ( ( 7 / 3 ) @ ( 10 / 3 ) ) ].
self assert: [ ( 1 / 3 ) - ( 1 @ 2 ) = ( ( -2 / 3 ) @ ( -5 / 3 ) ) ].
self assert: [ ( 1 / 3 ) * ( 1 @ 2 ) = ( ( 1 / 3 ) @ ( 2 / 3 ) ) ].
self assert: [ ( 1 / 3 ) / ( 1 @ 2 ) = ( ( 1 / 3 ) @ ( 1 / 6 ) ) ].
self assert: [ ( 1 / 3 ) // ( 1 @ 2 ) = ( 0 @ 0 ) ].
self assert: [ ( 1 / 3 ) % ( 1 @ 2 ) = ( ( 1 / 3 ) @ ( 1 / 3 ) ) ].
!
testFunctions
self assert: [ ( 1 / 3 ) negated = ( -1 / 3 ) ].
!
CLASS TestInteger EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ 42 class = Integer ].
self assert: [ ( Integer fromJs: INLINE '7' ) class = Integer ].
self assert: [ #( 2 3 4 ) includes: ( Integer randomFrom: 2 to: 4 ) ].
!
testConversion
self assert: [ 123 toString = '123' ].
self assert: [ 123 toBigInt class = BigInt ].
self assert: [ 8 toFloat = 8.0 ].
!
testComparisons
self assert: [ 11 = 11 ].
self assert: [ 11 <= 11 ].
self assert: [ 11 >= 11 ].
self assert: [ 11 < 12 ].
self assert: [ 13 > 11 ].
"Comparisons with large integer arguments."
self assert: [ 11 = 11 toBigInt ].
self assert: [ 11 <= 11 toBigInt ].
self assert: [ 11 >= 11 toBigInt ].
self assert: [ 11 < 12 toBigInt ].
self assert: [ 13 > 11 toBigInt ].
"Comparisons with fractions."
self assert: [ 2 = ( 4 / 2 ) ].
self assert: [ 2 >= ( 4 / 2 ) ].
self assert: [ 2 <= ( 4 / 2 ) ].
self assert: [ 2 < ( 7 / 3 ) ].
self assert: [ 2 > ( 5 / 3 ) ].
"Comparisons with floats."
self assert: [ 2 = 2.0 ].
self assert: [ 2 >= 2.0 ].
self assert: [ 2 >= 2.0 ].
self assert: [ 2 < 2.1 ].
self assert: [ 2 > 1.95 ].
!
testBasicArithmatic
"Basic arithmatic with integer arguments."
self assert: [ 3 + 4 = 7 ].
self assert: [ 4 - 3 = 1 ].
self assert: [ 3 * 4 = 12 ].
self assert: [ 4 / 3 = ( 4 / 3 ) ].
self assert: [ 7 // 3 = 2 ].
self assert: [ 7 % 3 = 1 ].
self assert: [ 7 ** 3 = 343 ].
"Basic arithmatic with large integer arguments."
self assert: [ 3 + 4 toBigInt = 7 ].
self assert: [ 4 - 3 toBigInt = 1 ].
self assert: [ 3 * 4 toBigInt = 12 ].
self assert: [ 4 / 3 toBigInt = ( 4 / 3 ) ].
self assert: [ 7 // 3 toBigInt = 2 ].
self assert: [ 7 % 3 toBigInt = 1 ].
self assert: [ 7 ** 3 toBigInt = 343 toBigInt ].
"Basic arithmatic with fractions."
self assert: [ 3 + ( 1 / 2 ) = ( 7 / 2 ) ].
self assert: [ 4 - ( 1 / 2 ) = ( 7 / 2 ) ].
self assert: [ 4 * ( 1 / 2 ) = 2 ].
self assert: [ 2 / ( 1 / 2 ) = 4 ].
self assert: [ 3 // ( 2 / 3 ) = 4 ].
self assert: [ 2 % ( 4 / 3 ) = ( 2 / 3 ) ].
self assert: [ 4 ** ( 1 / 2 ) equals: 2.00 precision: 0.01 ].
"Basic arithmatic with floats."
self assert: [ 3 + 0.5 = 3.5 ].
self assert: [ 4 - 0.5 = 3.5 ].
self assert: [ 4 * 0.5 = 2.0 ].
self assert: [ 2 / 0.5 = 4 ].
self assert: [ 3 // 0.8 = 3.0 ].
self assert: [ 2 % 0.8 = 0.4 ].
self assert: [ 2 ** 2.0 equals: 4.00 precision: 0.01 ].
"Basic arithmatic with points."
self assert: [ 3 + ( 1 @ 2 ) = ( 4 @ 5 ) ].
self assert: [ 4 - ( 1 @ 2 ) = ( 3 @ 2 ) ].
self assert: [ 2 * ( 2 @ 3 ) = ( 4 @ 6 ) ].
self assert: [ 6 / ( 2 @ 3 ) = ( 3 @ 2 ) ].
self assert: [ 7 // ( 2 @ 3 ) = ( 3 @ 2 ) ].
self assert: [ 8 % ( 2 @ 3 ) = ( 0 @ 2 ) ].
!
testBitOperations
self assert: [ 6 & 3 = 2 ].
self assert: [ 6 | 3 = 7 ].
self assert: [ ( 6 xor: 3 ) = 5 ].
self assert: [ 7 >> 1 = 3 ].
self assert: [ 3 << 1 = 6 ].
!
testGcd
self assert: [ ( 12 gcd: 9 ) = 3 ].
self assert: [ ( 12 gcd: 9 toBigInt ) = 3 ].
!
CLASS TestMagnitude EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ ( 1 min: 2 ) = 1 ].
self assert: [ ( 4 min: 3 ) = 3 ].
self assert: [ ( 5 max: 6 ) = 6 ].
self assert: [ ( 8 max: 7 ) = 8 ].
!
CLASS TestNumber EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ 5 squared = 25 ].
self assert: [ 25 isSquare ].
self assert: [ 26 isSquare not ].
self assert: [ 4 sqrt = 2.0 ].
self assert: [ 3 negated = -3 ].
self assert: [ -3 abs = 3 ].
self assert: [ 8 \\ 3 = 2 ].
self assertError: [ 1 / 0 ].
!
CLASS TestPoint EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ ( Point x: 1 y: 2 ) = ( 1 @ 2 ) ].
self assert: [ ( Point jsX: ( 1 js ) jsY: ( 2 js ) ) = ( 1 @ 2 ) ].
!
testConversion
self assert: [ ( 1 @ 2 ) toString = '( 1 @ 2 )' ].
!
testAccessing
self assert: [ ( Point x: 1 y: 2 ) x = 1 ].
self assert: [ ( Point x: 1 y: 2 ) y = 2 ].
self assert: [ ( ( 1 @ 2 ) x: 4 ) x = 4 ].
self assert: [ ( ( 1 @ 2 ) y: 5 ) y = 5 ].
!
testComparisons
self assert: [ ( 1 @ 2 ) = ( 1 @ 2 ) ].
self assert: [ ( 1 @ 2 ) <= ( 1 @ 2 ) ].
self assert: [ ( 1 @ 2 ) >= ( 1 @ 2 ) ].
self assert: [ ( 1 @ 2 ) < ( 2 @ 3 ) ].
self assert: [ ( 2 @ 3 ) > ( 1 @ 2 ) ].
!
testBasicArithmatic
self assert: [ ( 1 @ 2 ) + ( 3 @ 4 ) = ( 4 @ 6 ) ].
self assert: [ ( 3 @ 4 ) - ( 1 @ 2 ) = ( 2 @ 2 ) ].
self assert: [ ( 1 @ 2 ) * ( 3 @ 4 ) = ( 3 @ 8 ) ].
self assert: [ ( 4 @ 8 ) / ( 2 @ 4 ) = ( 2 @ 2 ) ].
self assert: [ ( 4 @ 8 ) // ( 3 @ 5 ) = ( 1 @ 1 ) ].
self assert: [ ( 4 @ 8 ) % ( 3 @ 5 ) = ( 1 @ 3 ) ].
"Basic arithmatic with integer arguments."
self assert: [ ( 1 @ 2 ) + 3 = ( 4 @ 5 ) ].
self assert: [ ( 3 @ 4 ) - 2 = ( 1 @ 2 ) ].
self assert: [ ( 1 @ 2 ) * 3 = ( 3 @ 6 ) ].
self assert: [ ( 4 @ 8 ) / 2 = ( 2 @ 4 ) ].
self assert: [ ( 4 @ 8 ) // 3 = ( 1 @ 2 ) ].
self assert: [ ( 4 @ 8 ) % 3 = ( 1 @ 2 ) ].
"Basic arithmatic with large integer arguments."
self assert: [ ( 1 @ 2 ) + 3 toBigInt = ( 4 @ 5 ) ].
self assert: [ ( 3 @ 4 ) - 2 toBigInt = ( 1 @ 2 ) ].
self assert: [ ( 1 @ 2 ) * 3 toBigInt = ( 3 @ 6 ) ].
self assert: [ ( 4 @ 8 ) / 2 toBigInt = ( 2 @ 4 ) ].
self assert: [ ( 4 @ 8 ) // 3 toBigInt = ( 1 @ 2 ) ].
self assert: [ ( 4 @ 8 ) % 3 toBigInt = ( 1 @ 2 ) ].
"Basic arithmatic with float arguments."
self assert: [ ( 1 @ 2 ) + 3.5 = ( 4.5 @ 5.5 ) ].
self assert: [ ( 3 @ 4 ) - 2.5 = ( 0.5 @ 1.5 ) ].
self assert: [ ( 1 @ 2 ) * 1.5 = ( 1.5 @ 3.0 ) ].
self assert: [ ( 3 @ 7 ) / 2.0 = ( 1.5 @ 3.5 ) ].
self assert: [ ( 4 @ 8 ) // 3.5 = ( 1.0 @ 2.0 ) ].
self assert: [ ( 4 @ 8 ) % 3.5 = ( 0.5 @ 1.0 ) ].
"Basic arithmatic with fraction arguments."
self assert: [ ( 1 @ 2 ) + ( 1 / 2 ) = ( ( 3 / 2 ) @ ( 5 / 2 ) ) ].
self assert: [ ( 3 @ 4 ) - ( 1 / 2 ) = ( ( 5 / 2 ) @ ( 7 / 2 ) ) ].
self assert: [ ( 1 @ 2 ) * ( 1 / 2 ) = ( ( 1 / 2 ) @ 1 ) ].
self assert: [ ( 3 @ 7 ) / ( 1 / 2 ) = ( 6 @ 14 ) ].
self assert: [ ( 4 @ 8 ) // ( 4 / 3 ) = ( 3 @ 6 ) ].
self assert: [ ( 1 @ 2 ) % ( 2 / 3 ) = ( ( 1 / 3 ) @ 0 ) ].
!
testFunctions
self assert: [ ( 2 @ 3 ) negated = ( -2 @ -3 ) ].
self assert: [ ( -2 @ -3 ) abs = ( 2 @ 3 ) ].
self assert: [ ( ( 2 @ 3 ) dotProduct: ( 4 @ 5 ) ) = 23 ].
!
CLASS TestPoint3d EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ ( Point3d x: 1 y: 2 z: 3 ) = ( 1 @ 2 @ 3 ) ].
self assert: [ ( Point3d jsX: ( 1 js ) jsY: ( 2 js ) jsZ: ( 3 js ) ) = ( 1 @ 2 @ 3 ) ].
!
testConversion
self assert: [ ( 1 @ 2 @ 3 ) toString = '( 1 @ 2 @ 3 )' ].
!
testAccessing
| point3d |
point3d := Point3d x: 1 y: 2 z: 3.
self assert: [ point3d x = 1 ].
self assert: [ point3d y = 2 ].
self assert: [ point3d z = 3 ].
point3d := 4 @ 5 @ 6.
self assert: [ point3d x = 4 ].
self assert: [ point3d y = 5 ].
self assert: [ point3d z = 6 ].
!
testComparisons
self assert: [ ( 1 @ 2 @ 3 ) = ( 1 @ 2 @ 3 ) ].
self assert: [ ( 1 @ 2 @ 3 ) <= ( 1 @ 2 @ 3 ) ].
self assert: [ ( 1 @ 2 @ 3 ) >= ( 1 @ 2 @ 3 ) ].
self assert: [ ( 1 @ 2 @ 3 ) < ( 2 @ 3 @ 4 ) ].
self assert: [ ( 2 @ 3 @ 4 ) > ( 1 @ 2 @ 3 ) ].
!
testBasicArithmatic
"Basic arithmatic with Point3d arguments."
self assert: [ ( 1 @ 2 @ 3 ) + ( 4 @ 5 @ 6 ) = ( 5 @ 7 @ 9 ) ].
self assert: [ ( 4 @ 5 @ 6 ) - ( 1 @ 2 @ 3 ) = ( 3 @ 3 @ 3 ) ].
self assert: [ ( 1 @ 2 @ 3 ) * ( 4 @ 5 @ 6 ) = ( 4 @ 10 @ 18 ) ].
self assert: [ ( 4 @ 8 @ 16 ) / ( 2 @ 4 @ 8 ) = ( 2 @ 2 @ 2 ) ].
self assert: [ ( 4 @ 8 @ 16 ) // ( 3 @ 5 @ 9 ) = ( 1 @ 1 @ 1 ) ].
self assert: [ ( 4 @ 8 @ 12 ) % ( 3 @ 5 @ 7 ) = ( 1 @ 3 @ 5 ) ].
"Basic arithmatic with Integer arguments."
self assert: [ ( 1 @ 2 @ 3 ) + 4 = ( 5 @ 6 @ 7 ) ].
self assert: [ ( 3 @ 4 @ 5 ) - 2 = ( 1 @ 2 @ 3 ) ].
self assert: [ ( 1 @ 2 @ 3 ) * 4 = ( 4 @ 8 @ 12 ) ].
self assert: [ ( 4 @ 8 @ 12 ) / 2 = ( 2 @ 4 @ 6 ) ].
self assert: [ ( 4 @ 8 @ 10 ) // 3 = ( 1 @ 2 @ 3 ) ].
self assert: [ ( 4 @ 8 @ 12 ) % 3 = ( 1 @ 2 @ 0 ) ].
"Basic arithmatic with large integer arguments."
self assert: [ ( 1 @ 2 @ 3 ) + 4 toBigInt = ( 5 @ 6 @ 7 ) ].
self assert: [ ( 5 @ 6 @ 7 ) - 2 toBigInt = ( 3 @ 4 @ 5 ) ].
self assert: [ ( 1 @ 2 @ 3 ) * 3 toBigInt = ( 3 @ 6 @ 9 ) ].
self assert: [ ( 4 @ 8 @ 12 ) / 2 toBigInt = ( 2 @ 4 @ 6 ) ].
self assert: [ ( 4 @ 8 @ 12 ) // 3 toBigInt = ( 1 @ 2 @ 4 ) ].
self assert: [ ( 4 @ 8 @ 12 ) % 3 toBigInt = ( 1 @ 2 @ 0 ) ].
"Basic arithmatic with float arguments."
self assert: [ ( 1 @ 2 @ 3 ) + 3.5 = ( 4.5 @ 5.5 @ 6.5 ) ].
self assert: [ ( 3 @ 4 @ 5 ) - 2.5 = ( 0.5 @ 1.5 @ 2.5 ) ].
self assert: [ ( 1 @ 2 @ 3 ) * 1.5 = ( 1.5 @ 3.0 @ 4.5 ) ].
self assert: [ ( 3 @ 7 @ 8 ) / 2.0 = ( 1.5 @ 3.5 @ 4.0 ) ].
self assert: [ ( 4 @ 8 @ 12 ) // 3.5 = ( 1.0 @ 2.0 @ 3.0 ) ].
self assert: [ ( 4 @ 8 @ 12 ) % 3.5 = ( 0.5 @ 1.0 @ 1.5 ) ].
"Basic arithmatic with fraction arguments."
self assert: [ ( 1 @ 2 @ 3 ) + ( 1 / 2 ) = ( ( 3 / 2 ) @ ( 5 / 2 ) @ ( 7 / 2 ) ) ].
self assert: [ ( 3 @ 4 @ 5 ) - ( 1 / 2 ) = ( ( 5 / 2 ) @ ( 7 / 2 ) @ ( 9 / 2 ) ) ].
self assert: [ ( 1 @ 2 @ 3 ) * ( 1 / 2 ) = ( ( 1 / 2 ) @ 1 @ ( 3 / 2 ) ) ].
self assert: [ ( 3 @ 7 @ 9 ) / ( 1 / 2 ) = ( 6 @ 14 @ 18 ) ].
self assert: [ ( 4 @ 8 @ 12 ) // ( 4 / 3 ) = ( 3 @ 6 @ 9 ) ].
self assert: [ ( 1 @ 2 @ 3 ) % ( 2 / 3 ) = ( ( 1 / 3 ) @ 0 @ ( 1 / 3 ) ) ].
!
testFunctions
self assert: [ ( 2 @ 3 @ 4 ) negated = ( -2 @ -3 @ -4 ) ].
self assert: [ ( -2 @ -3 @ -4 ) abs = ( 2 @ 3 @ 4 ) ].
self assert: [ ( ( 2 @ 3 @ 4 ) dotProduct: ( 4 @ 5 @ 6 ) ) = 47 ].
!
CLASS TestRectangle EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreatiing
self assert: [ ( Rect origin: ( 1 @ 2 ) extent: ( 3 @ 4 ) ) class = Rect ].
"Method fromJs: jsDOMRect can only be tested in a browser environment."
!
testConverting
self assert: [ ( Rect origin: ( 1 @ 2 ) extent: ( 3 @ 4 ) ) toString = 'Rect( ( 1 @ 2 ) , ( 3 @ 4 ) )' ].
!
testAccessing
| rectangle |
rectangle := Rect new origin: ( 1 @ 2 ) extent: ( 3 @ 4 ).
self assert: [ rectangle origin = ( 1 @ 2 ) ].
self assert: [ rectangle extent = ( 3 @ 4 ) ].
!
testComparing
self assert: [ ( Rect origin: ( 1 @ 2 ) extent: ( 3 @ 4 ) ) = ( Rect origin: ( 1 @ 2 ) extent: ( 3 @ 4 ) ) ].
!
CLASS TestString EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
self assert: [ String new = '' ].
self assert: [ ( String fromJs: INLINE '"a"' ) = 'a' ].
self assert: [ ( String fromCharacter: $A ) = 'A' ].
self assert: [ ( String fromCharCode: 66 ) = 'B' ].
self assert: [ ( ( String newline ) at: 0 ) code = 10 ].
self assert: [ 'a' toString = 'a' ].
!
testConversion
self assert: [ 'abc' toString = 'abc' ].
self assert: [ '42' toInteger = 42 ].
self assert: [ '3.14' toFloat = 3.14 ].
!
testAccessing
self assert: [ 'abcd' length = 4 ].
self assert: [ ( 'abc' at: 1 ) = $b ].
self assertError: [ 'abc' at: 10 ].
self assert: [ ( 'azc' at: 1 put: $b ) = 'abc' ].
!
testRegularExpressions
self assert: [ ( 'This is It!' match: '[A-Z].' flags: 'g' ) = #( 'Th' 'It' ) ].
self assert: [ ( 'This is It!' search: '[A-I].' ) = 8 ].
!
testIteration
| sum |
sum := 0.
'ABC' do: [ :char |
sum := sum + char code ].
self assert: [ sum = 198 ].
!
testSearching
self assert: [ 'abcde' includes: 'bc' ].
self assert: [ ( 'abcde' includes: 'bc' from: 3 ) not ].
self assert: [ 'abcde' startsWith: 'abc' ].
self assert: [ 'abcde' endsWith: 'de' ].
self assert: [ ( 'abcde' indexOf: 'cd' ) = 2 ].
self assert: [ ( 'abcabcabc' indexOf: 'abc' from: 1 ) = 3 ].
self assert: [ ( 'abcabcabc' lastIndexOf: 'abc' ) = 6 ].
self assert: [ ( 'abcabcabc' lastIndexOf: 'abcd' ) = -1 ].
self assert: [ ( 'abcabcabc' lastIndexOf: 'abc' from: 4 ) = 3 ].
!
testComparisons
self assert: [ 'aa', 'bb' = 'aabb' ].
self assert: [ 'aa' ~= 'bb' ].
self assert: [ 'a' ~= 1 ].
self assert: [ 'a' ~= Object new ].
self assert: [ 'a' < 'b' ].
self assert: [ 'a' <= 'a' ].
self assert: [ 'b' > 'a' ].
self assert: [ 'b' >= 'b' ].
self assert: [ ( 'a' localeCompare: 'b' ) = -1 ].
!
testManipulation
self assert: [ 'aa', 'bb' = 'aabb' ].
self assert: [ 'cc' + 'dd' = 'ccdd' ].
self assert: [ ( 'de' concat: 'f' ) = 'def' ].
self assert: [ ( 'aabb' substring: 1 ) = 'abb' ].
self assert: [ ( 'aabb' substring: 1 to: 3 ) = 'ab' ].
self assert: [ ( 'abcd' slice: 2 ) = 'cd' ].
self assert: [ ( 'abcd' slice: 1 to: 3 ) = 'bc' ].
self assert: [ ( ( 'This is it' split: ' ' ) at: 1 ) = 'is' ].
self assert: [ ( 'abc' padEnd: 5 ) = 'abc ' ].
self assert: [ ( 'def' padEnd: 6 with: 'x' ) = 'defxxx' ].
self assert: [ ( 'abc' padStart: 5 ) = ' abc' ].
self assert: [ ( 'def' padStart: 6 with: 'x' ) = 'xxxdef' ].
self assert: [ ( 'ab' repeat: 3 ) = 'ababab' ].
self assert: [ ( 'abab' replace: 'b' with: 'd' ) = 'adab' ].
self assert: [ ( 'abab' replaceAll: 'b' with: 'd' ) = 'adad' ].
self assert: [ ' abc ' trim = 'abc' ].
self assert: [ ' abc' trimStart = 'abc' ].
self assert: [ 'abc ' trimEnd = 'abc' ].
!
testCase
self assert: [ 'abc' toUpperCase = 'ABC' ].
self assert: [ 'abc' toLocaleUpperCase = 'ABC' ].
self assert: [ 'ABC' toLowerCase = 'abc' ].
self assert: [ 'ABC' toLocaleLowerCase = 'abc' ].
!
testEscapedChars
| string |
string := '\b\f\n\r\t\v'.
self assert: [ string length = 6 ].
self assert: [ ( string at: 0 ) = 8 ].
self assert: [ ( string at: 1 ) = 12 ].
self assert: [ ( string at: 2 ) = 10 ].
self assert: [ ( string at: 3 ) = 13 ].
self assert: [ ( string at: 4 ) = 9 ].
self assert: [ ( string at: 5 ) = 11 ].
string := '\\'.
self assert: [ string length = 1 & ( ( string at: 0 ) = 92 ) ].
string := '"'.
self assert: [ string length = 1 & ( ( string at: 0 ) = 34 ) ].
string := '\''.
self assert: [ string length = 1 & ( ( string at: 0 ) = 39 ) ].
self assert: [ 'Hello world!' isWellFormed ].
self assert: [ 'Hello world \uD800' isWellFormed not ].
self assert: [ 'Hello world!' toWellFormed = 'Hello world!' ].
self assert: [ 'Hello world \uD800' toWellFormed isWellFormed ].
!
CLASS TestAbort EXTENDS Test MODULE Core CLASSVARS '' VARS ''
"Tests AbortSignal and AbortController"
testCreation
| signal depenentSignal |
self assertError: [ AbortSignal new ].
signal := AbortSignal abort.
self assert: [ signal aborted ].
depenentSignal := AbortSignal any: #( signal ).
self assert: [ signal aborted ].
!
testFetchAbort
| controller requestInit response signal |
"This test is disabled by default, because the VSCode debugger halts
on the rejected promise after the intentionally aborted fetch."
^ self.
controller := AbortController new.
requestInit := RequestInit new
signal: controller signal.
"Don't await the fetch, but immediately abort it after starting."
Promise fromJs: ( Fetch request: 'https://jsonplaceholder.typicode.com/posts/1' options: requestInit )
then: [ :response | self onFetchAbort: response ]
catch: [ :error | self onFetchAbortError: error ].
controller abort.
signal := controller signal.
self assert: [ signal aborted ].
self assert: [ signal reason name = 'AbortError' ].
!
onFetchAbort: response
"This should not be called, the fetch request should have been aborted."
self Error: 'Fetch was not aborted'.
!
onFetchAbortError: error
"This error is intended behavior, caused by an aborted fetch."
self assert: [ error name = 'AbortError' ].
!
testFetchTimedAbort
| requestInit |
"This test is disabled by default, because the VSCode debugger halts
on the rejected promise after the intentionally aborted fetch."
^ self.
requestInit := RequestInit new
signal: ( AbortSignal timeout: 0 ).
Promise fromJs: ( Fetch request: 'https://jsonplaceholder.typicode.com/posts/1' options: requestInit )
then: [ :response | self onFetchTimedAbort: response ]
catch: [ :error | self onFetchTimedAbortError: error ].
!
onFetchTimedAbort: response
"This should not be called, the fetch request should have been aborted."
self Error: 'Fetch was not aborted'.
!
onFetchTimedAbortError: error
"This error is intended behavior, caused by an aborted fetch."
self assert: [ error name = 'TimeoutError' ].
!
CLASS TestFetch EXTENDS Test MODULE Core CLASSVARS '' VARS ''
async test
"The test API: https://jsonplaceholder.typicode.com/posts/1
Gives response body: (will have double quotes for the strings):
{ 'userId' : 1, 'id': 1, 'title': 'sunt aut facere ...', 'body': 'quia et suscipit ...' }"
| url response text object |
url := 'https://jsonplaceholder.typicode.com/posts/1'.
response := await Fetch request: url.
self assert: [ response ok ].
text := await Fetch text: url.
self assert: [ text includes: 'title' ].
object := await Fetch object: url.
self assert: [ ( object atProperty: 'id' ) = 1 ].
!
CLASS TestFormData EXTENDS Test MODULE Core CLASSVARS '' VARS ''
test
| formData |
formData := FormData new.
self assert: [ formData class = FormData ].
formData append: 'name' value: 'Alice'.
formData append: 'address' value: 'Church st'.
self assert: [ formData has: 'name' ].
self assert: [ ( formData has: 'xname' ) not ].
self assert: [ formData keys = #( 'name' 'address') ].
self assert: [ formData values = #( 'Alice' 'Church st') ].
self assert: [ formData entries = #( #( 'name' 'Alice' ) #( 'address' 'Church st') ) ].
formData append: 'name' value: 'Bob'.
self assert: [ ( formData get: 'name' ) = 'Alice' ].
self assert: [ ( formData getAll: 'name' ) = #( 'Alice' 'Bob' ) ].
formData set: 'address' value: 'Penny ln'.
self assert: [ ( formData get: 'address' ) = 'Penny ln' ].
formData delete: 'address'.
self assert: [ formData keys = #( 'name' 'name' ) ].
!
testBlob
| formData blob blob2 |
formData := FormData new.
blob := Blob fromArray: ( Uint8Array from: #( 4 5 6 7 ) ).
formData append: 'blob' value: blob.
blob2 := formData get: 'blob'.
self assert: [ blob2 size = 4 ]
!
"TODO: Test creation with HtmlFormElement in Browser tests."
CLASS TestHeaders EXTENDS Test MODULE Core CLASSVARS '' VARS ''
test
| headers entries |
headers := Headers new.
self assert: [ headers class = Headers ].
self assert: [ headers jsClassName = 'Headers' ].
entries := #(
#( 'set-cookie' 'greeting=hello' )
#( 'x-custom-header' 'name=world' ) ).
headers := Headers init: entries.
self assert: [ headers entries = entries ].
self assert: [ headers keys = #( 'set-cookie' 'x-custom-header' ) ].
self assert: [ headers values = #( 'greeting=hello' 'name=world' ) ].
self assert: [ headers getSetCookie = #( 'greeting=hello' ) ].
headers append: 'x-header' value: 'value'.
self assert: [ ( headers get: 'x-header' ) = 'value' ].
headers set: 'x-header' value: 'new-value'.
self assert: [ ( headers get: 'x-header' ) = 'value, new-value' ].
self assert: [ headers has: 'x-header' ].
headers delete: 'x-header'.
self assert: [ ( headers has: 'x-header' ) not ].
!
CLASS TestRequest EXTENDS Test MODULE Core CLASSVARS '' VARS ''
testDefaults
| request |
self assertError: [ Request new ].
request := Request url: 'http://unknown.com'.
self assert: [ request bodyUsed not ].
self assert: [ request cache = 'default' ].
self assert: [ request credentials = 'same-origin' ].
self assert: [ request destination = '' ].
self assert: [ request headers keys length = 0 ].
self assert: [ request integrity = '' ].
self assert: [ request keepalive not ].
self assert: [ request method = 'GET' ].
self assert: [ request mode = 'cors' ].
self assert: [ request redirect = 'follow' ].
self assert: [ request referrer = 'about:client' ].
self assert: [ request referrerPolicy = '' ].
self assert: [ request signal aborted not ].
self assert: [ request url includes: 'unknown.com' ].
!
testInitialized
| requestInit request |
requestInit := RequestInit new
body: 'request body';
cache: 'reload';
credentials: 'omit';
headers: ( Headers new append: 'x-custom-header' value: 'custom value' );
integrity: 'sha256-abc123';
keepalive: true;
method: 'POST';
mode: 'same-origin';
redirect: 'manual';
referrer: 'http://referrer.com';
referrerPolicy: 'origin';
signal: ( AbortController new signal ).
request := Request url: 'http://unknown.com' options: requestInit.
self assert: [ request bodyUsed not ].
self assert: [ request cache = 'reload' ].
self assert: [ request credentials = 'omit' ].
self assert: [ request destination = '' ].
self assert: [ ( request headers get: 'x-custom-header' ) = 'custom value' ].
self assert: [ request integrity = 'sha256-abc123' ].
self assert: [ request keepalive ].
self assert: [ request method = 'POST' ].
self assert: [ request mode = 'same-origin' ].
self assert: [ request redirect = 'manual' ].
"Note: Browsers do not copy the referrer set in requestInit."
self assert: [ #( 'http://referrer.com/' 'about:client' ) includes: request referrer ].
self assert: [ request referrerPolicy = 'origin' ].
self assert: [ request signal aborted not ].
self assert: [ request url includes: 'unknown.com' ].
!
async testMethods
| url requestInit request arrayBuffer blob bytes text object formData |
url := 'http://unknown.com'.
requestInit := RequestInit new
method: 'POST';
body: 'request body';
integrity: 'sha256-abc123'.
request := Request url: url options: requestInit.
self assert: [ request clone integrity = request integrity ].
"Note: 2025-07-29:
The method Request.clone in Node.js does *not* create re-readable requests (error: unusable),
so a new request is made for every buffer reading."
request := Request url: url options: requestInit.
arrayBuffer := await request arrayBuffer.
self assert: [ arrayBuffer byteLength = 12 ].
request := Request url: url options: requestInit.
blob := await request blob.
self assert: [ blob size = 12 ].
request := Request url: url options: requestInit.
bytes := await request bytes.
self assert: [ bytes byteLength = 12 ].
request := Request url: url options: requestInit.
text := await request text.
self assert: [ text = 'request body' ].
requestInit body: '{ "a": 1 }'.
request := Request url: url options: requestInit.
object := await request json.
self assert: [ ( object atProperty: 'a' ) = 1 ].
requestInit body: ( FormData new append: 'b' value: 2 ).
request := Request url: url options: requestInit.
formData := await request formData.
self assert: [ ( formData get: 'b' ) = 2 ].
!
CLASS TestRequestInit EXTENDS Test MODULE Core CLASSVARS '' VARS ''
test
| requestInit |
requestInit := RequestInit new.
self assert: [ requestInit isEmpty ].
requestInit method: 'POST'.
self assert: [ requestInit method = 'POST' ].
requestInit headers: Headers new.
self assert: [ requestInit headers class = Headers ].
requestInit body: ( ArrayBuffer new: 8 ).
self assert: [ requestInit body byteLength = 8 ].
requestInit mode: 'cors'.
self assert: [ requestInit mode = 'cors' ].
requestInit credentials: 'same-origin'.
self assert: [ requestInit credentials = 'same-origin' ].
requestInit cache: 'reload'.
self assert: [ requestInit cache = 'reload' ].
requestInit redirect: 'follow'.
self assert: [ requestInit redirect = 'follow' ].
requestInit referrer: ''.
self assert: [ requestInit referrer = '' ].
requestInit referrerPolicy: 'no-referrer'.
self assert: [ requestInit referrerPolicy = 'no-referrer' ].
requestInit integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE='.
self assert: [ requestInit integrity = 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=' ].
requestInit keepalive: false.
self assert: [ requestInit keepalive not ].
requestInit signal: AbortController new signal.
self assert: [ requestInit signal class = AbortSignal ].
requestInit priority: 'high'.
self assert: [ requestInit priority = 'high' ].
!
CLASS TestResponse EXTENDS Test MODULE Core CLASSVARS '' VARS ''
async test
| url response headers text bytes arrayBuffer blob object |
"The test API: https://jsonplaceholder.typicode.com/posts/1
Gives response body: (will have double quotes for the strings):
{ 'userId' : 1, 'id': 1, 'title': 'sunt aut facere ...', 'body': 'quia et suscipit ...' }"
url := 'https://jsonplaceholder.typicode.com/posts/1'.
response := await Fetch request: url.
self assert: [ response body class = ReadableStream ].
self assert: [ response bodyUsed not ].
headers := response headers.
self assert: [ headers class = Headers ].
self assert: [ ( headers get: 'content-type' ) includes: 'application' ].
self assert: [ response ok ].
self assert: [ response status = 200 ].
"Browsers give default status empty string iso 'OK'"
self assert: [ #( 'OK' '' ) includes: response statusText ].
self assert: [ response statusAndText includes: '200' ].
self assert: [ response redirected not ].
"Browsers give deault type 'cors' iso 'basic'"
self assert: [ #( 'basic' 'cors' ) includes: response type ].
self assert: [ response url = url ].
text := await response clone text.
self assert: [ text includes: 'title' ].
bytes := await response clone bytes.
self assert: [ ( bytes at: 0 ) = 123 ].
self assert: [ ( bytes at: 1 ) = 10 ].
arrayBuffer := await response clone arrayBuffer.
self assert: [ arrayBuffer byteLength = 292 ].
blob := await response clone blob.
self assert: [ blob size = 292 ].
object := await response clone json.
self assert: [ ( object atProperty: 'id' ) = 1 ].
"formDataThen: is not tested here,
because the is no suitable API present in API test site typicode.com."
!
CLASS TestEventTarget EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
CLASS TestPointerEvent EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
CLASS TestCrypto EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| buffer |
buffer := Uint8Array new: 8.
Crypto randomValues: buffer.
self assert: [ buffer toArray ~= #( 0 0 0 0 0 0 0 0 ) ].
self assert: [ Crypto randomUuid length = 36 ].
!
async testDigest
| plainText plainData digestBuffer digestData |
plainText := 'secret'.
plainData := Uint8Array encodeFromString: plainText.
digestBuffer := await Crypto digest: 'SHA-256' data: plainData.
self assert: [ digestBuffer class = ArrayBuffer ].
self assert: [ digestBuffer byteLength = 32 ].
digestData := Uint8Array buffer: digestBuffer.
self assert: [ digestData toHex = '2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b' ].
!
CLASS TestCryptoAes EXTENDS Test MODULE TestCrypto CLASSVARS ''
VARS 'keyUsages key'
"Tests the AES encryption algorithm: AES-GCM"
async test
await self generateKey.
await self encryptDecrypt.
await self exportImportKey.
await self wrapUnwrapKey.
!
async generateKey
| params |
params := AesKeyGenParams new
name: 'AES-GCM';
length: 256.
keyUsages := #( 'encrypt' 'decrypt' 'wrapKey' 'unwrapKey' ).
key := await Crypto generateKey: params extractable: true usages: keyUsages.
self checkKey: key.
!
checkKey: key
| params |
self assert: [ key class = CryptoKey ].
self assert: [ key type = 'secret' ].
self assert: [ key extractable ].
self assert: [ key usages = keyUsages ].
params := key algorithm.
self assert: [ params class = AesKeyGenParams ].
self assert: [ params name = 'AES-GCM' ].
self assert: [ params length = 256 ].
!
async encryptDecrypt
| params plainText plainData encryptedBuffer decryptedBuffer decryptedData decryptedText |
"params also contains the 'iv' 12 byte nonce, needed for decrypting."
params := AesGcmParams new.
plainText := 'Hello, AES-GCM!'.
plainData := Uint8Array encodeFromString: plainText.
encryptedBuffer := await Crypto encrypt: params key: key data: plainData.
decryptedBuffer := await Crypto decrypt: params key: key data: encryptedBuffer.
decryptedData := Uint8Array buffer: decryptedBuffer.
decryptedText := decryptedData decodeToString.
self assert: [ decryptedText = plainText ].
!
async exportImportKey
| exportedKey importedKey |
"Export import in raw format."
exportedKey := await Crypto exportKey: 'raw' key: key.
self assert: [ exportedKey class = ArrayBuffer ].
self assert: [ exportedKey byteLength = 32 ].
importedKey := await Crypto importKey: 'raw' keyData: exportedKey
algorithm: key algorithm extractable: true usages: keyUsages.
self checkKey: importedKey.
"Export import in jwk format."
exportedKey := await Crypto exportKey: 'jwk' key: key.
self assert: [ exportedKey class = JsObject ].
self assert: [ ( exportedKey atJsProperty: 'alg' ) = 'A256GCM' ].
importedKey := await Crypto importKey: 'jwk' keyData: exportedKey
algorithm: key algorithm extractable: true usages: keyUsages.
self checkKey: importedKey.
!
async wrapUnwrapKey
| params wrappedKey unwrappedKey |
"params also has iv random bits that need to be used for unwrapping."
params := AesGcmParams new.
wrappedKey := await Crypto wrapKey: 'jwk' key: key wrappingKey: key algorithm: params.
self assert: [ wrappedKey class = ArrayBuffer ].
self assert: [ wrappedKey byteLength = 160 ].
unwrappedKey := await Crypto unwrapKey: 'jwk' key: wrappedKey unwrappingKey: key unwrapAlgo: params
algorithm: key algorithm extractable: true usages: keyUsages.
self checkKey: unwrappedKey.
!
CLASS TestCryptoEcdh EXTENDS Test MODULE TestCrypto CLASSVARS ''
VARS 'algorithm keyPair'
"Tests the key exchange algorithm ECDH."
async testGenerateKey
| params usages privateKey publicKey |
algorithm := 'ECDH'.
params := EcKeyGenParams new
name: algorithm;
namedCurve: 'P-384'.
usages := #( 'deriveKey' 'deriveBits' ).
keyPair := await Crypto generateKey: params extractable: true usages: usages.
self assert: [ keyPair class = CryptoKeyPair ].
privateKey := keyPair privateKey.
self assert: [ privateKey class = CryptoKey ].
self assert: [ privateKey type = 'private' ].
self assert: [ privateKey extractable ].
self assert: [ privateKey usages = usages ].
self checkParams: privateKey algorithm.
publicKey := keyPair publicKey.
self assert: [ publicKey class = CryptoKey ].
self assert: [ publicKey type = 'public' ].
self assert: [ privateKey extractable ].
self assert: [ publicKey usages = #() ].
self checkParams: publicKey algorithm.
await self deriveKey.
!
checkParams: params
self assert: [ params class = EcKeyGenParams ].
self assert: [ params name = algorithm ].
self assert: [ params namedCurve = 'P-384' ].
!
async deriveKey
| params derivedKeyParams derivedKeyUsages derivedKey derivedBits |
params := EcdhKeyDeriveParams new
name: 'ECDH';
public: keyPair publicKey.
"Derive AES key."
derivedKeyParams := AesKeyGenParams new
name: 'AES-CBC';
length: 256.
derivedKeyUsages := #( 'encrypt' 'decrypt' ).
derivedKey := await Crypto deriveKey: params key: keyPair privateKey
type: derivedKeyParams extractable: true usages: derivedKeyUsages.
self assert: [ derivedKey class = CryptoKey ].
self assert: [ derivedKey type = 'secret' ].
self assert: [ derivedKey extractable ].
self assert: [ derivedKey usages = derivedKeyUsages ].
derivedKeyParams := derivedKey algorithm.
self assert: [ derivedKeyParams class = AesKeyGenParams ].
self assert: [ derivedKeyParams name = 'AES-CBC' ].
self assert: [ derivedKeyParams length = 256 ].
"Derive bits."
derivedBits := await Crypto deriveBits: params key: keyPair privateKey length: 256.
self assert: [ derivedBits class = ArrayBuffer ].
self assert: [ derivedBits byteLength = 32 ].
!
CLASS TestCryptoRsa EXTENDS Test MODULE TestCrypto CLASSVARS ''
VARS 'algorithm keyPair'
"Tests the RSA encryption algorithm RSA-OAEP."
async testGenerateKey
| params privateKey publicKey |
algorithm := 'RSA-OAEP'.
params := RsaKeyGenParams new
name: algorithm;
modulusLength: 2048;
publicExponent: ( Uint8Array from: #( 1 0 1 ) );
hash: 'SHA-256'.
keyPair := await Crypto generateKey: params extractable: true usages: #( 'encrypt' 'decrypt' ).
self assert: [ keyPair class = CryptoKeyPair ].
privateKey := keyPair privateKey.
self assert: [ privateKey class = CryptoKey ].
self assert: [ privateKey type = 'private' ].
self assert: [ privateKey extractable ].
self assert: [ privateKey usages = #( 'decrypt' ) ].
self checkParams: privateKey algorithm.
publicKey := keyPair publicKey.
self assert: [ publicKey class = CryptoKey ].
self assert: [ publicKey type = 'public' ].
self assert: [ privateKey extractable ].
self assert: [ publicKey usages = #( 'encrypt' ) ].
self checkParams: publicKey algorithm.
await self encryptDecrypt.
!
checkParams: params
self assert: [ params class = RsaKeyGenParams ].
self assert: [ params name = algorithm ].
self assert: [ params modulusLength = 2048 ].
self assert: [ params publicExponent = ( Uint8Array from: #( 1 0 1 ) ) ].
!
async encryptDecrypt
| plainText plainData encryptedBuffer decryptedBuffer decryptedData decryptedText |
plainText := 'Hello, RSA-OAEP!'.
plainData := Uint8Array encodeFromString: plainText.
encryptedBuffer := await Crypto encrypt: algorithm key: keyPair publicKey data: plainData.
decryptedBuffer := await Crypto decrypt: algorithm key: keyPair privateKey data: encryptedBuffer.
decryptedData := Uint8Array buffer: decryptedBuffer.
decryptedText := decryptedData decodeToString.
self assert: [ decryptedText = plainText ].
!
CLASS TestCryptoRsaSigning EXTENDS Test MODULE TestCrypto CLASSVARS ''
VARS 'algorithm keyPair'
"Tests the RSA signing algorithm RSA-PSS."
async testGenerateKey
| params privateKey publicKey |
algorithm := 'RSA-PSS'.
params := RsaKeyGenParams new
name: algorithm;
modulusLength: 2048;
publicExponent: ( Uint8Array from: #( 1 0 1 ) );
hash: 'SHA-256'.
keyPair := await Crypto generateKey: params extractable: true usages: #( 'sign' 'verify' ).
self assert: [ keyPair class = CryptoKeyPair ].
privateKey := keyPair privateKey.
self assert: [ privateKey class = CryptoKey ].
self assert: [ privateKey type = 'private' ].
self assert: [ privateKey extractable ].
self assert: [ privateKey usages = #( 'sign' ) ].
self checkParams: privateKey algorithm.
publicKey := keyPair publicKey.
self assert: [ publicKey class = CryptoKey ].
self assert: [ publicKey type = 'public' ].
self assert: [ publicKey usages = #( 'verify' ) ].
self checkParams: publicKey algorithm.
await self signVerify.
!
checkParams: params
self assert: [ params class = RsaKeyGenParams ].
self assert: [ params name = algorithm ].
self assert: [ params modulusLength = 2048 ].
self assert: [ params publicExponent = ( Uint8Array from: #( 1 0 1 ) ) ].
!
async signVerify
| params plainText plainData signatureBuffer verified |
params := RsaPssParams new saltLength: 32.
plainText := 'Hello, RSA-PSS!'.
plainData := Uint8Array encodeFromString: plainText.
signatureBuffer := await Crypto sign: params key: keyPair privateKey data: plainData.
verified := await Crypto verify: params key: keyPair publicKey signature: signatureBuffer data: plainData.
self assert: [ verified ].
!
CLASS TestAesGcmParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params iv additionalData |
params := AesGcmParams new.
self assert: [ params class = AesGcmParams ].
self assert: [ params name = 'AES-GCM' ].
iv := Uint8Array new: 12.
Crypto randomValues: iv.
params iv: iv.
self assert: [ params iv = iv ].
additionalData := Uint8Array new: 8.
params additionalData: additionalData.
self assert: [ params additionalData = additionalData ].
params tagLength: 64.
self assert: [ params tagLength = 64 ].
!
CLASS TestAesKeyGenParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params |
params := AesKeyGenParams new.
self assert: [ params class = AesKeyGenParams ].
params name: 'AES-GCM'.
self assert: [ params name = 'AES-GCM' ].
params length: 256.
self assert: [ params length = 256 ].
!
CLASS TestEcdhKeyDeriveParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params key |
params := EcdhKeyDeriveParams new.
self assert: [ params class = EcdhKeyDeriveParams ].
params name: 'ECDH'.
self assert: [ params name = 'ECDH' ].
key = CryptoKey new.
params public: key.
self assert: [ params public = key ].
!
CLASS TestEcKeyGenParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params |
params := EcKeyGenParams new.
self assert: [ params class = EcKeyGenParams ].
params name: 'ECDH'.
self assert: [ params name = 'ECDH' ].
params namedCurve: 'P-512'.
self assert: [ params namedCurve = 'P-512' ].
!
CLASS TestRsaKeyGenParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params exponent |
params := RsaKeyGenParams new.
self assert: [ params class = RsaKeyGenParams ].
params name: 'RSA-OAEP'.
self assert: [ params name = 'RSA-OAEP' ].
params modulusLength: 2048.
self assert: [ params modulusLength = 2048 ].
exponent := Uint8Array from: #( 1 0 1 ).
params publicExponent: exponent.
self assert: [ params publicExponent = exponent ].
params hash: 'SHA-256'.
self assert: [ params hash = 'SHA-256' ].
!
CLASS TestRsaOaepParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params buffer |
params := RsaOaepParams new.
self assert: [ params class = RsaOaepParams ].
self assert: [ params name = 'RSA-OAEP' ].
buffer := ArrayBuffer new: 8.
params label: buffer.
self assert: [ params label = buffer ].
!
CLASS TestRsaPssParams EXTENDS Test MODULE TestCrypto CLASSVARS '' VARS ''
test
| params |
params := RsaPssParams new.
self assert: [ params class = RsaPssParams ].
self assert: [ params name = 'RSA-PSS' ].
params saltLength: 32.
self assert: [ params saltLength = 32 ].
!
CLASS TestDictionary EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| dict string array |
dict := Dictionary new.
dict at: 'a' put: 'aValue'.
dict at: 'b' put: 'bValue'.
dict at: 'c' put: 'xValue'.
"Replace value at key 'c'"
dict at: 'c' put: 'cValue'.
self assert: [ dict toString = 'Dictionary( { a, aValue } { b, bValue } { c, cValue } )' ].
self assert: [ dict size = 3 ].
self assert: [ dict keyValues first key = 'a' ].
self assert: [ dict has: 'a' ].
self assert: [ ( dict find: 'a' ) key = 'a' ].
self assert: [ ( dict at: 'a' ) = 'aValue' ].
self assertError: [ dict at: 'z' ].
self assert: [ ( dict at: 'b' ifAbsent: [ nil ] ) = 'bValue' ].
self assert: [ ( dict at: 'z' ifAbsent: [ nil ] ) = nil ].
dict removeAt: 'b'.
self assert: [ dict size = 2 ].
string := ''.
dict do: [ :key :value |
string := string, key, value ].
self assert: [ string = 'aaValueccValue' ].
dict clear.
self assert: [ dict size = 0 ].
array := #( #( 'a' 1 ) #( 'b' 2 ) ).
dict := Dictionary fromArray: array.
self assert: [ dict size = 2 ].
self assert: [ ( dict at: 'a' ) = 1 ].
self assert: [ ( dict at: 'b' ) = 2 ].
array := dict toArray.
self assert: [ array = #( #( 'a' 1 ) #( 'b' 2 ) ) ]
!
CLASS TestKeyValue EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| keyValue |
keyValue := KeyValue key: 'a' value: 'aValue'.
self assert: [ keyValue class = KeyValue ].
self assert: [ keyValue toString = '{ a, aValue }' ].
self assert: [ keyValue key = 'a' ].
self assert: [ keyValue value = 'aValue' ].
keyValue key: 'b'.
self assert: [ keyValue key = 'b' ].
keyValue value: 'bValue'.
self assert: [ keyValue value = 'bValue' ].
!
CLASS TestMap EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| map sum count array |
map := Map new.
self assert: [ map class = Map ].
map set: 'a' value: 1.
map set: 'b' value: 2.
map set: 'c' value: 9.
"Replace value at key 'c'"
map set: 'c' value: 3.
self assert: [ map size = 3 ].
self assert: [ ( map get: 'c' ) = 3 ].
self assert: [ ( map get: 'z' ) = nil ].
self assert: [ map has: 'b' ].
self assert: [ ( map has: 'd' ) not ].
self assert: [ map delete: 'b' ].
self assert: [ ( map has: 'b' ) not ].
map set: 'b' value: 22.
self assert: [ ( map get: 'b' ) = 22 ].
map set: 'b' value: 2.
self assert: [ ( map get: 'b' ) = 2 ].
self assert: [ map keys length = 3 ].
self assert: [ ( map keys at: 0 ) = 'a' ].
self assert: [ ( map values at: 2 ) = 2 ].
self assert: [ ( ( map entries at: 2 ) at: 0 ) = 'b' ].
self assert: [ ( ( map entries at: 2 ) at: 1 ) = 2 ].
sum := 0.
map forEach: [ :key :value |
self assert: [ #( 'a' 'b' 'c' ) includes: key ].
self assert: [ value > 0 ].
sum := sum + value ].
self assert: [ sum = 6 ].
count := 0.
map forEach: [ :key :value |
count := count + 1 ].
self assert: [ count = 3 ].
array := #( #( 'a' 1 ) #( 'b' 2 ) ).
map := Map fromArray: array.
self assert: [ map size = 2 ].
self assert: [ ( map get: 'a' ) = 1 ].
self assert: [ ( map get: 'b' ) = 2 ].
array := map toArray.
self assert: [ array = #( #( 'a' 1 ) #( 'b' 2 ) ) ].
map clear.
self assert: [ map size = 0 ].
!
testGroupBy
| groupBlock map v |
groupBlock := [ :element | element % 2 = 0 ifTrue: [ 'even' ] ifFalse: [ 'odd' ] ].
map := Map group: #( 1 2 3 4 5 ) by: groupBlock.
self assert: [ map size = 2 ].
v := map get: 'even'.
self assert: [ ( map get: 'even' ) = #( 2 4 ) ].
self assert: [ ( map get: 'odd' ) = #( 1 3 5 ) ].
!
CLASS TestSet EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| set set2 set3 string |
set := Set new.
set add: 'a'.
set add: 'b'.
set add: 'c'.
self assert: [ set size = 3 ].
self assert: [ set values = #( 'a' 'b' 'c' ) ].
self assert: [ set keys = set values ].
self assert: [ set entries = #( #( 'a' 'a' ) #( 'b' 'b' ) #( 'c' 'c' ) ) ].
self assert: [ set toString = 'Set( a b c )' ].
self assert: [ ( set find: 'b' ) = 'b' ].
self assert: [ ( set find: 'd' ) isNil ].
self assert: [ set has: 'b' ].
self assert: [ ( set has: 'd' ) not ].
string := ''.
set forEach: [ :value | string := string + value ].
self assert: [ string = 'abc' ].
set2 := Set new: #( 'c' 'd' ).
self assert: [ ( set difference: set2 ) values = #( 'a' 'b' ) ].
self assert: [ ( set intersection: set2 ) values = #( 'c' ) ].
self assert: [ ( set symmetricDifference: set2 ) values = #( 'a' 'b' 'd' ) ].
self assert: [ ( set union: set2 ) values = #( 'a' 'b' 'c' 'd' ) ].
set3 := Set new: #( 'e' ).
self assert: [ set isDisjointFrom: set3 ].
self assert: [ ( set isDisjointFrom: set2 ) not ].
set3 := Set new: #( 'a' 'b' ).
self assert: [ set3 isSubsetOf: set ].
self assert: [ ( set isSubsetOf: set3 ) not ].
self assert: [ set isSupersetOf: set3 ].
self assert: [ ( set3 isSupersetOf: set2 ) not ].
set delete: 'b'.
self assert: [ set size = 2 ].
self assert: [ set values = #( 'a' 'c' ) ].
set clear.
self assert: [ set size = 0 ].
!
CLASS TestArray EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCore
| array1 array2 value |
array1 := Array new: 3.
array2 := #( 4 5 6 ).
value := 7.
self assert: [ array1 length + array2 length = 6 ].
self assert: [ #( 1 'a' 3.14 value ( 4 + 5 ) ) toString = '#( 1 a 3.14 7 9 )' ].
self assert: [ ( #( 1 2 3 ) join: ',' ) = '1,2,3' ].
self assert: [ array1 = #( nil nil nil ) ].
self assert: [ array2 = #( 4 5 6 ) ].
self assert: [ ( array2 at: 1 ) = 5 ].
array2 at: 2 put: 7.
self assert: [ ( array2 at: 2 ) = 7 ].
array2 add: 8.
self assert: [ array2 length = 4 ].
self assert: [ ( #( 1 2 3 ) at: 1 with: 9 ) = #( 1 9 3 ) ].
self assert: [ #( 1 2 ) , #( 3 4 ) = #( 1 2 3 4 ) ].
self assert: [ array2 pop = 8 ].
self assertError: [ Array new pop ].
array1 := #( 1 2 ).
array2 := array1 copy.
self assert: [ array1 = array2 ].
array2 add: 3.
self assert: [ array1 ~= array2 ].
!
testCreation
| array jsArray |
self assert: [ Array new length = 0 ].
self assert: [ ( Array new: 3 ) length = 3 ].
self assert: [ ( Array with: 7 ) first = 7 ].
self assert: [ ( Array with: 8 with: 9 ) last = 9 ].
array := Array fromJs: INLINE '[ 4, 5, 6 ]'.
"Array now contains JS numbers, not ST numbers."
self assert: [ ( Integer fromJs: ( array at: 1 ) ) = 5 ].
array := Array fromJs: INLINE '[ 6, 7, 8 ]' elementClass: Integer.
self assert: [ ( array at: 1 ) = 7 ].
array := Array fromJs: INLINE '[ 10, 11, 12 ]' elementConverter:
[ :element | ( Integer fromJs: element ) * 10 ].
self assert: [ ( array at: 1 ) = 110 ].
jsArray := ( Array with: 13 with: 'a' ) toJs.
self assert: [ ( Integer fromJs: INLINE 'jsArray[ 0 ]' ) = 13 ].
self assert: [ ( String fromJs: INLINE 'jsArray[ 1 ]' ) = 'a' ].
!
testSearch
| array |
array := #( 5 6 7 8 9 ).
self assert: [ ( array filter: [ :element | element % 2 = 0 ] ) = #( 6 8 ) ].
self assert: [ ( array find: [ :element | element = 8 ] ) = 8 ].
self assert: [ ( array find: [ :element | element = 10 ] ) = nil ].
self assert: [ ( array findIndex: [ :element | element = 8 ] ) = 3 ].
self assert: [ ( array findIndex: [ :element | element = 10 ] ) = -1 ].
self assert: [ ( array findLastIndex: [ :element | element % 2 = 0 ] ) = 3 ].
self assert: [ ( array findLastIndex: [ :element | element >= 10 ] ) = -1 ].
self assert: [ ( array indexOf: 6 ) = 1 ].
self assert: [ ( array indexOf: 3 ) = -1 ].
self assert: [ array includes: 8 ].
self assert: [ ( array includes: 10 ) not ].
!
testModify
| array |
array := #( 5 6 7 ).
self assert: [ ( array map: [ :element | element squared ] ) = #( 25 36 49 ) ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] ) = 18 ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] with: 100 ) = 118 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] ) = -4 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] with: 100 ) = 82 ].
self assert: [ array copy reverse = #( 7 6 5 ) ].
self assert: [ array toReversed = #( 7 6 5 ) ].
self assert: [ ( array shift = 5 ) & ( array = #( 6 7 ) ) ].
self assert: [ ( array unshift: 5 ) = #( 5 6 7 ) ].
self assert: [ ( array slice: 1 ) = #( 6 7 ) ].
self assert: [ ( array slice: 0 to: 2 ) = #( 5 6 ) ].
self assert: [ ( array copy splice: 1 ) = #( 5 ) ].
self assert: [ ( array copy splice: 1 count: 1 ) = #( 5 7 ) ].
self assert: [ ( array toSpliced: 1 ) = #( 5 ) ].
self assert: [ ( array toSpliced: 1 count: 1 ) = #( 5 7 ) ].
self assert: [ ( array copy swap: 0 with: 2 ) = #( 7 6 5 ) ].
self assert: [ ( #( 4 5 6 ) removeAt: 1 ) = #( 4 6 ) ].
self assert: [ ( array copy copyWithin: 0 start: 1 end: 3 ) = #( 6 7 7 ) ].
!
testIteration
| array sum |
array := #( 4 5 6 ).
sum := 0.
array do: [ :num | sum := sum + num ].
self assert: [ sum = 15 ].
self assert: [ array every: [ :element | element > 3 ] ].
self assert: [ ( array every: [ :element | element <= 5 ] ) not ].
self assert: [ array some: [ :element | element > 5 ] ].
self assert: [ ( array some: [ :element | element > 6 ] ) not ].
!
testSort
| reverseCompareBlock |
self assert: [ #( 9 8 7 6 5 4 3 2 1 0 ) sort = #( 0 1 2 3 4 5 6 7 8 9 ) ].
self assert: [ #( 10 90 32 74 34 57 89 61 32 44 ) sort = #( 10 32 32 34 44 57 61 74 89 90 ) ].
self assert: [ #( 9 8 7 6 5 4 3 2 1 0 ) toSorted = #( 0 1 2 3 4 5 6 7 8 9 ) ].
self assert: [ #( 10 90 32 74 34 57 89 61 32 44 ) toSorted = #( 10 32 32 34 44 57 61 74 89 90 ) ].
reverseCompareBlock := [ :a :b | b compare: a ].
self assert: [ ( #( 10 90 32 74 34 57 89 61 32 44 ) sortWith: reverseCompareBlock ) = #( 90 89 74 61 57 44 34 32 32 10 ) ].
self assert: [ ( #( 10 90 32 74 34 57 89 61 32 44 ) toSortedWith: reverseCompareBlock ) = #( 90 89 74 61 57 44 34 32 32 10 ) ].
!
testRandomize
| array randomArray sum |
array := Array new.
1 to: 30 do: [ :index |
array add: index ].
randomArray := array copy randomize.
"The chance of the arrays now being equal is 1E-32."
self assert: [ randomArray ~= array ].
sum := randomArray reduce: [ :element1 :element2 | element1 + element2 ].
self assert: [ sum = ( 31 * 30 / 2 ) ].
!
CLASS TestArrayBuffer EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| arrayBuffer |
arrayBuffer := ArrayBuffer new: 8.
self assert: [ arrayBuffer class = ArrayBuffer ].
self assert: [ arrayBuffer byteLength = 8 ].
!
CLASS TestFloat16Array EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
| array |
self assert: [ Float16Array new length = 0 ].
self assert: [ ( Float16Array new: 3 ) length = 3 ].
self assert: [ ( Float16Array new: 3 ) last = 0 ].
self assert: [ ( Float16Array with: 7.0 ) first = 7.0 ].
self assert: [ ( Float16Array with: 8.0 with: 9.0 ) last = 9.0 ].
array := Float16Array from: #( -400.0 5.0 6000.0 ).
self assert: [ ( array at: 1 ) = 5.0 ].
!
testConversion
| array |
array := Float16Array from: #( -400.0 5.0 6000.0 ).
self assert: [ array toArray = #( -400.0 5.0 6000.0 ) ].
self assert: [ ( array entries at: 1 ) = #( 1 5.0 ) ].
self assert: [ ( array join: ' ' ) = '-400 5 6000' ].
self assert: [ array toString = '#( -400 5 6000 )' ].
!
testBuffer
| array |
array := Float16Array from: #( -400.0 5.0 6000.0 ).
self assert: [ ( ArrayBuffer isView: array ) not ].
self assert: [ array buffer class = ArrayBuffer ].
self assert: [ array buffer byteLength = 6 ].
self assert: [ ( array slice: 1 to: 2 ) first = 5.0 ].
!
testCore
| array |
array := Float16Array new: 3.
self assert: [ array toArray = #( 0.0 0.0 0.0 ) ].
self assert: [ array length = 3 ].
self assert: [ ( array at: 0 ) = 0.0 ].
array at: 1 put: -400.0.
self assert: [ ( array at: 1 ) = -400.0 ].
array at: 2 put: 6000.
self assert: [ ( array at: 2 ) = 6000.0 ].
array := Float16Array from: #( -400.0 5.0 6000.0 ).
self assert: [ array length = 3 ].
self assert: [ array toArray = #( -400.0 5.0 6000.0 ) ].
self assert: [ array toString includes: '6000' ].
!
testSearch
| array |
array := Float16Array from: #( 5.0 6.0 7.0 8.0 9.0 ).
self assert: [ ( array filter: [ :element | element % 2 = 0 ] ) toArray = #( 6.0 8.0 ) ].
self assert: [ ( array find: [ :element | element = 8.0 ] ) = 8.0 ].
self assert: [ ( array find: [ :element | element = 10.0 ] ) = nil ].
self assert: [ ( array findIndex: [ :element | element = 8.0 ] ) = 3 ].
self assert: [ ( array findIndex: [ :element | element = 10.0 ] ) = -1 ].
self assert: [ ( array findLast: [ :element | element < 8.0 ] ) = 7.0 ].
self assert: [ ( array findLast: [ :element | element >= 10.0 ] ) = nil ].
self assert: [ ( array findLastIndex: [ :element | element < 8.0 ] ) = 2 ].
self assert: [ ( array findLastIndex: [ :element | element >= 10.0 ] ) = -1 ].
self assert: [ ( array indexOf: 6.0 ) = 1 ].
self assert: [ ( array indexOf: 3.0 ) = -1 ].
self assert: [ ( ( Float16Array from: #( 3.0 3.0 4.0 ) ) lastIndexOf: 3.0 ) = 1 ].
self assert: [ ( ( Float16Array from: #( 3.0 3.0 4.0 ) ) lastIndexOf: 5.0 ) = -1 ].
self assert: [ array includes: 8.0 ].
self assert: [ ( array includes: 10.0 ) not ].
!
testIteration
| array sum |
array := #( 4.0 5.0 6.0 ).
sum := 0.
array do: [ :num | sum := sum + num ].
self assert: [ sum = 15.0 ].
self assert: [ array every: [ :element | element >= 4.0 ] ].
self assert: [ ( array every: [ :element | element <= 5.0 ] ) not ].
self assert: [ array some: [ :element | element > 5.0 ] ].
self assert: [ ( array some: [ :element | element > 6.0 ] ) not ].
!
testModification
| array |
array := Float16Array from: #( 5.0 6.0 7.0 ).
self assert: [ ( array map: [ :element | element squared ] ) toArray = #( 25.0 36.0 49.0 ) ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] ) = 18.0 ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] with: 100.0 ) = 118.0 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] ) = -4.0 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] with: 100.0 ) = 82.0 ].
self assert: [ ( array slice: 1 ) toArray = #( 6.0 7.0 ) ].
self assert: [ ( array slice: 0 to: 2 ) toArray = #( 5.0 6.0 ) ].
self assert: [ ( array copy swap: 0 with: 2 ) toArray = #( 7.0 6.0 5.0 ) ].
self assert: [ ( array copy copyWithin: 0 start: 1 end: 3 ) toArray = #( 6.0 7.0 7.0 ) ].
self assert: [ ( array copy fill: 9.0 start: 0 end: 2 ) toArray = #( 9.0 9.0 7.0 ) ].
self assert: [ array copy reverse toArray = #( 7.0 6.0 5.0 ) ].
self assert: [ ( array copy set: ( Float16Array from: #( 8.0 9.0 ) ) offset: 1 ) toArray = #( 5.0 8.0 9.0 ) ].
!
testSelection
| array |
array := Float16Array from: #( 5.0 6.0 7.0 ).
self assert: [ ( array subarray: 1 ) toArray = #( 6.0 7.0 ) ].
self assert: [ ( array subarray: 1 to: 2 ) toArray = #( 6.0 ) ].
!
testSort
self assert: [ #( 9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0 0.0 ) sort = #( 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 ) ].
self assert: [ #( 10.0 90.0 32.0 74.0 34.0 57.0 89.0 61.0 30.0 44.0 ) sort = #( 10.0 30.0 32.0 34.0 44.0 57.0 61.0 74.0 89.0 90.0 ) ].
!
CLASS TestUint8Array EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
testCreation
| array |
self assert: [ Uint8Array new length = 0 ].
self assert: [ ( Uint8Array new: 3 ) length = 3 ].
self assert: [ ( Uint8Array new: 3 ) last = 0 ].
self assert: [ ( Uint8Array with: 7 ) first = 7 ].
self assert: [ ( Uint8Array with: 8 with: 9 ) last = 9 ].
array := Uint8Array from: #( 4 5 6 ).
self assert: [ ( array at: 1 ) = 5 ].
!
testConversion
| array |
array := Uint8Array from: #( 4 5 6 ).
self assert: [ array toArray = #( 4 5 6 ) ].
self assert: [ ( array entries at: 1 ) = #( 1 5 ) ].
self assert: [ ( array join: ' ' ) = '4 5 6' ].
self assert: [ array toString = '#( 4 5 6 )' ].
self assert: [ array toHex = '040506' ].
self assert: [ ( Uint8Array fromHex: '4a4b4c' ) toArray = #( 74 75 76 ) ].
!
testCoding
| array |
array := Uint8Array encodeFromString: 'ABC'.
self assert: [ array toArray = #( 65 66 67 ) ].
self assert: [ array decodeToString = 'ABC' ].
!
testBuffer
| array |
array := Uint8Array from: #( 4 5 6 ).
self assert: [ ( ArrayBuffer isView: array ) not ].
self assert: [ array buffer class = ArrayBuffer ].
self assert: [ array buffer byteLength = 3 ].
self assert: [ ( array slice: 1 to: 2 ) first = 5 ].
!
testCore
| array1 array2 |
array1 := Uint8Array new: 3.
array2 := Uint8Array from: #( 4 5 6 ).
self assert: [ array1 toArray = #( 0 0 0 ) ].
self assert: [ array2 toArray = #( 4 5 6 ) ].
self assert: [ array1 length + array2 length = 6 ].
self assert: [ array2 toString = '#( 4 5 6 )' ].
self assert: [ ( array2 at: 1 ) = 5 ].
array2 at: 2 put: 7.
self assert: [ ( array2 at: 2 ) = 7 ].
array2 at: 0 put: 257.
self assert: [ ( array2 at: 0 ) = 1 ].
array2 at: 0 put: -1.
self assert: [ ( array2 at: 0 ) = 255 ].
!
testSearch
| array |
array := Uint8Array from: #( 5 6 7 8 9 ).
self assert: [ ( array filter: [ :element | element % 2 = 0 ] ) toArray = #( 6 8 ) ].
self assert: [ ( array find: [ :element | element = 8 ] ) = 8 ].
self assert: [ ( array find: [ :element | element = 10 ] ) = nil ].
self assert: [ ( array findIndex: [ :element | element = 8 ] ) = 3 ].
self assert: [ ( array findIndex: [ :element | element = 10 ] ) = -1 ].
self assert: [ ( array findLast: [ :element | element < 8 ] ) = 7 ].
self assert: [ ( array findLast: [ :element | element >= 10 ] ) = nil ].
self assert: [ ( array findLastIndex: [ :element | element < 8 ] ) = 2 ].
self assert: [ ( array findLastIndex: [ :element | element >= 10 ] ) = -1 ].
self assert: [ ( array indexOf: 6 ) = 1 ].
self assert: [ ( array indexOf: 3 ) = -1 ].
self assert: [ ( ( Uint8Array from: #( 3 3 4 ) ) lastIndexOf: 3 ) = 1 ].
self assert: [ ( ( Uint8Array from: #( 3 3 4 ) ) lastIndexOf: 5 ) = -1 ].
self assert: [ array includes: 8 ].
self assert: [ ( array includes: 10 ) not ].
!
testIteration
| array sum |
array := #( 4 5 6 ).
sum := 0.
array do: [ :num | sum := sum + num ].
self assert: [ sum = 15 ].
self assert: [ array every: [ :element | element >= 4 ] ].
self assert: [ ( array every: [ :element | element <= 5 ] ) not ].
self assert: [ array some: [ :element | element > 5 ] ].
self assert: [ ( array some: [ :element | element > 6 ] ) not ].
!
testModification
| array |
array := Uint8Array from: #( 5 6 7 ).
self assert: [ ( array map: [ :element | element squared ] ) toArray = #( 25 36 49 ) ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] ) = 18 ].
self assert: [ ( array reduce: [ :element1 :element2 | element1 + element2 ] with: 100 ) = 118 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] ) = -4 ].
self assert: [ ( array reduceRight: [ :element1 :element2 | element1 - element2 ] with: 100 ) = 82 ].
self assert: [ ( array slice: 1 ) toArray = #( 6 7 ) ].
self assert: [ ( array slice: 0 to: 2 ) toArray = #( 5 6 ) ].
self assert: [ ( array copy swap: 0 with: 2 ) toArray = #( 7 6 5 ) ].
self assert: [ ( array copy copyWithin: 0 start: 1 end: 3 ) toArray = #( 6 7 7 ) ].
self assert: [ ( array copy fill: 9 start: 0 end: 2 ) toArray = #( 9 9 7 ) ].
self assert: [ array copy reverse toArray = #( 7 6 5 ) ].
self assert: [ ( array copy set: ( Uint8Array from: #( 8 9 ) ) offset: 1 ) toArray = #( 5 8 9 ) ].
!
testSelection
| array |
array := Uint8Array from: #( 5 6 7 ).
self assert: [ ( array subarray: 1 ) toArray = #( 6 7 ) ].
self assert: [ ( array subarray: 1 to: 2 ) toArray = #( 6 ) ].
!
testSort
self assert: [ #( 9 8 7 6 5 4 3 2 1 0 ) sort = #( 0 1 2 3 4 5 6 7 8 9 ) ].
self assert: [ #( 10 90 32 74 34 57 89 61 30 44 ) sort = #( 10 30 32 34 44 57 61 74 89 90 ) ].
!
CLASSEXTENSION Object
"This is a sample extension of the Object class
to test the compiler EXTENSION feature."
CLASSMETHODS
extensionClassMethod
^ 'extensionClassMethod'.
!
METHODS
extensionMethod
^ 'extensionMethod'.
!
CLASS TestBlock EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| milliseconds num |
self assert: [ [ 1 + 2 ] value = 3 ].
self assert: [ ( [ :a | a squared ] value: 3 ) = 9 ].
self assert: [ ( [ :a :b | a * b ] value: 3 value: 4 ) = 12 ].
self assert: [ [ true ] and: [ true ] ].
self assert: [ [ false ] or: [ true ] ].
self assert: [ ( [ :x | x + 1 ] script ) includes: 'x.$$plus' ].
milliseconds := Date new.
[ Date new <= milliseconds ] whileTrue.
self assert: [ Date new > milliseconds ].
num := 0.
[ num < 3 ] whileTrue: [ num := num + 1 ].
self assert: [ num = 3 ].
num := [ | a | a := 1. a + 1 ] value.
self assert: [ num = 2 ].
async [ await 1 ] value.
"The following 2 commented lines would generate compiler errors
about using 'await' outside an async method or block."
"await 1."
"[ await 1 ] value."
!
async testValueAwait
| result |
result := await async [ await 1 + 2 ] valueAwait.
self assert: [ result = 3 ].
result := await async [ :a | await a + a ] valueAwait: 3.
self assert: [ result = 6 ].
result := await async [ :a :b | await a + b ] valueAwait: 3 value: 2.
self assert: [ result = 5 ].
!
async testWhileTrueAwait
| milliseconds num |
milliseconds := Date new.
await async [ await Timer timeout: 10.
Date new <= milliseconds ] whileTrueAwait.
self assert: [ Date new > milliseconds ].
num := 0.
await [ num < 3 ] whileTrueAwait:
async [ await Timer timeout: 10.
num := num + 1. ].
self assert: [ num = 3 ].
!
testTryCatch
| result |
result := [ Object missingMethod ]
tryCatch: [ :error | self onTryCatch: error ].
self assert: [ result = 'caught' ].
!
onTryCatch: error
self assert: [ error message includes: 'is not a function' ].
^ 'caught'.
!
async testTryAwaitCatch
| result |
result := await async [ Object missingMethod ]
tryAwaitCatch: [ :error | self onTryCatch: error ].
self assert: [ result = 'caught' ].
!
CLASS TestBoolean EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ true toString = 'true' ].
self assert: [ false toString = 'false' ].
self assert: [ true = true ].
self assert: [ true ~= false ].
self assert: [ false < true ].
self assert: [ false <= true ].
self assert: [ true > false ].
self assert: [ true >= false ].
self assert: [ true ifTrue: [ true ] ].
self assert: [ ( false ifTrue: [ true ] ifFalse: [ false ] ) not ].
self assert: [ false ifFalse: [ true ] ].
self assert: [ ( false ifFalse: [ false ] ifTrue: [ true ] ) not ].
self assert: [ true and: [ true ] ].
self assert: [ ( true and: [ false ] ) not ].
self assert: [ true or: [ false ] ].
self assert: [ ( false or: [ false ] ) not ].
self assert: [ true ].
self assert: [ false not ].
self assert: [ true & true ].
self assert: [ ( true & false ) not ].
self assert: [ true | false ].
self assert: [ ( false | false ) not ].
!
async testAsyncAwait
| trueBlock falseBlock result |
trueBlock := async [ await Timer timeout: 1. true ].
falseBlock := async [ await Timer timeout: 2. false ].
"The result cannot go directly into asserts with sync blocks."
self checkResult: ( await true ifTrueAwait: trueBlock ).
self checkResult: ( ( await false ifTrueAwait: trueBlock ifFalseAwait: falseBlock ) not ).
self checkResult: ( ( await false ifFalseAwait: falseBlock ) not ).
self checkResult: ( await true ifFalseAwait: [ falseBlock ] ifTrueAwait: trueBlock ).
!
checkResult: result
self assert: [ result ].
!
CLASS TestClass EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ Class name = 'Class' ].
self assert: [ Class toString = 'class Class' ].
self assert: [ Class superclass = Object ].
self assert: [ Class name = 'Class' ].
self assert: [ Class methodNames includes: 'name' ].
self assert: [ ( Class methodNames includes: 'xname' ) not ].
self assert: [ Class classMethodNames includes: 'classes' ].
self assert: [ Boolean methodNames includes: '|' ].
self assert: [ Boolean methodNames includes: 'not' ].
self assert: [ Boolean canUnderstand: 'not' ].
self assert: [ Boolean canUnderstand: 'isNil' ].
self assert: [ ( Boolean canUnderstand: 'XisNil' ) not ].
!
testCompiler
| a_b |
a_b := 3.
self assert: [ a_b = 3 ].
!
_test_reservered_words
| delete |
delete := true.
self assert: [ delete ].
!
CLASS TestCompiler EXTENDS Test MODULE TestCore CLASSVARS 'c1'
VARS 'v1
v2'
"This class tests some compiler parsing features"
CLASSMETHODS
testClass
self assert: [ c1 isNil ].
!
METHODS
test
self assert: [ c1 = v1 ].
self assert: [ v1 = v2 ].
self assert: [ 1+2=3 ].
self assert: [ self A: 1 b: 2 ].
!
A:a b:b
^ true.
!
testClassExtension
"These will succeed if ObjectExtension.st was successfully compiled."
self assert: [ Object extensionClassMethod = 'extensionClassMethod' ].
self assert: [ Object new extensionMethod = 'extensionMethod' ].
!
CLASS TestConsole EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
"The JS console object produces only output / side effects that cannot be read back.
So the tests in this class should be visually isnpected.
And de test are disabled by default, so the don't mess up other logging.
Enable tests by replacing 'noTest' with 'test' or by calling 'noTest' explicitly."
METHODS
"This test is disabled default by calling it 'noTest',
so console testing massages do not obscure other loggings."
noTest
| label |
Console log: 'This log message should be cleared.'.
Console clear.
Console assert: false value: 'This assert message should be shown.'.
Console assert: true value: 'This assert message should NOT be shown.'.
Console assert: true value: 'This assert message should NOT be shown.'.
Console debug: 'Debug mesage.'.
Console error: 'Error message.'.
Console info: 'Info message.'.
Console log: 'Log message.'.
Console warn: 'Warning message.'.
label := 'label3'.
Console count: label.
Console countReset: label.
Console count: label.
Console count: label.
Console count: label.
Console dir: 'aString'.
Console dirxml: 'anXmlString'.
Console table: #( 'A' 'B' 'C' ).
label := 'groupLabel'.
Console group: label.
Console log: 'At level 1'.
Console groupCollapsed: label.
Console log: 'Collapsed at level 2'.
Console groupEnd: label.
Console groupEnd: label.
Console log: 'Back at level 0'.
label := 'timeLabel'.
Console time: label.
Console timeStamp: label.
Console timeLog: label.
Console timeEnd: label.
Console log: 'The following stack trace is explicitly requested:'.
Console trace.
!
CLASS TestDebugger EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
"This class contains some code set breakpoints in and step through,
to see if the debugger is working as desired."
test1
^ 1 + 2.
!
test2
| a b |
a := 1.
b := a + 2.
"self log: 'Debugger finished'."
!
CLASS TestError EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| error cause |
self assertError: [ Error throw: 'My error' ].
[ Error throw: 'My error' ] tryCatch: [ :error | self onError: error ].
error := Error new.
self assert: [ error message = '' ].
error message: 'My message'.
self assert: [ error message = 'My message' ].
error := Error new: 'New error'.
self assert: [ error message = 'New error' ].
self assertError: [ error throw ].
self assert: [ error cause isNil ].
cause := Error new: 'Error cause'.
error cause: cause.
self assert: [ error cause message = 'Error cause' ].
error := Error new: 'Error with cause' cause: cause.
self assert: [ error cause message = 'Error cause' ].
!
onError: error
self assert: [ error message = 'My error' ].
self assert: [ error name = 'Error' ].
self assert: [ error toString = 'Error: Error: My error' ].
!
CLASS TestJsObject EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| jsObject |
self assert: [ JsObject new isUndefined ].
self assert: [ JsObject newEmpty isEmpty ].
self assert: [ ( JsObject fromJs: INLINE 'null' ) isNull ].
self assert: [ ( JsObject fromJs: INLINE 'undefined' ) isNull ].
self assert: [ JsObject newEmpty toString = 'a JsObject( [object Object] )' ].
self assert: [ ( JsObject new js: INLINE '123' ) toString = 'a JsObject( 123 )' ].
jsObject := JsObject fromJs: INLINE '{
name: "John Doe", age: 50, pi: 3.14, itIs: true, empty: null,
things: [ 1, "b", 3.3 ], nested: { inHere: "I am in here" } }'.
self assert: [ ( jsObject atJsProperty: 'name' ) = 'John Doe' ].
self assert: [ ( jsObject atJsProperty: 'age' ) = 50 ].
self assert: [ ( jsObject atJsProperty: 'pi' ) = 3.14 ].
self assert: [ ( jsObject atJsProperty: 'itIs' ) = true ].
self assert: [ ( jsObject atJsProperty: 'empty' ) = nil ].
self assert: [ ( ( jsObject atJsProperty: 'things' ) at: 1 ) = 'b' ].
self assert: [ ( ( jsObject atJsProperty: 'nested' ) atProperty: 'inHere' ) = 'I am in here' ].
self assert: [ jsObject hasOwn: 'age' ].
self assert: [ ( jsObject hasOwn: 'missing' ) not ].
self assert: [ Date new jsClassName = 'Date' ].
self assert: [ true jsType = 'boolean' ].
!
testJson
| object |
object := Object fromJson: '{ "a": 1, "b": { "b1": 21 }, "c": [ 31, 32 ] }'.
self assert: [ ( object atProperty: 'a' ) = 1 ].
self assert: [ ( ( object atProperty: 'b' ) atProperty: 'b1' ) = 21 ].
self assert: [ ( object atProperty: 'c' ) = #( 31 32 ) ].
!
CLASS TestNil EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
self assert: [ nil = nil ].
self assert: [ nil class = Nil ].
self assert: [ nil isNull ].
self assert: [ nil isNil ].
self assert: [ nil toString = 'nil' ].
self assert: [ ( Nil fromJs: INLINE 'null' ) = nil ].
self assert: [ ( Nil fromJs: INLINE 'undefined' ) = nil ].
self assert: [ ( Nil fromJs: 3 ) = 3 ].
self assert: [ nil ifNil: [ true ] ].
self assert: [ 1 ifNotNil: [ true ] ].
!
CLASS TestObject EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| object |
object := Object new.
self assert: [ object isMemberOf: Object ].
self assert: [ String new isKindOf: Object ].
self assert: [ self isKindOf: Test ].
self assert: [ 10 respondsTo: '+' ].
self assert: [ object = object ].
self assert: [ object == object ].
self assert: [ object ~= Object new ].
self assert: [ true ].
self assertError: [ object error: 'My error.' ].
self assertError: [ object subclassResponsibility ].
self assertError: [ object halt ].
self assert: [ object isNil not ].
self assert: [ object notNil ].
self assert: [ object yourself = object ].
!
testConversion
| object js |
object := Object new.
self assert: [ object toString = 'a Object' ].
"JS cannot compare objects for value quality, so use toJson for implecitly testing toJs."
self assert: [ object toJson = '{}' ].
!
testPerform
| object |
object := Object new.
self assert: [ ( object perform: 'toString' ) = 'a Object' ].
self assert: [ object perform: '=' with: object ].
self assert: [ ( 'abc' perform: 'substring:to:' with: 1 with: 2 ) = 'b' ].
self assert: [ ( Date perform: 'year:month:day:' with: 1980 with: 2 with: 28 ) year = 1980 ].
!
testAssignment
| object1 object2 |
object1 := 7.
self assert: [ object1 = 7 ].
object2 := 'a'.
self assert: [ object2 = 'a' ].
object1 := object2 := 2.0.
self assert: [ ( object1 = 2.0 ) & ( object2 = 2.0 ) ].
!
testProperties
| point |
point := ( 2 @ 3 ).
self assert: [ point keys = #( 'x' 'y' ) ].
self assert: [ point entries first = #( 'x' 2 ) ].
self assert: [ point ownPropertyNames = #( 'x' 'y' ) ].
self assert: [ ( point atProperty: 'x' ) = 2 ].
self assert: [ ( point atProperty: 'z' ) isNil ].
point atProperty: 'y' put: 4.
self assert: [ ( point atProperty: 'y' ) = 4 ].
!
CLASS TestPromise EXTENDS Test MODULE TestCore CLASSVARS '' VARS ''
test
| promise |
promise := Promise new: [ :resolve :reject |
Timer timeout: 10 then: [ resolve value: 42 ] ].
promise then: [ :value | self assert: [ value = 42 ] ].
promise := Promise resolve: 'ok'.
promise then: [ :value | self assert: [ value = 'ok' ] ].
promise finally: [ self assert: [ true ] ].
!
testCollections
| promise1 promise2 result |
promise1 := Promise resolve: 42.
promise2 := Promise resolve: 'ok'.
result := Promise all: #( promise1 promise2 )
then: [ :values | self assert: [ values = #( 42 'ok' ) ] ].
self assert: [ result class = Promise ].
result := Promise allSettled: #( promise1 promise2 ) then: [ :promiseStatuses |
self assert: [ promiseStatuses length = 2 ].
self assert: [ promiseStatuses first status = 'fulfilled' ].
self assert: [ promiseStatuses first value = 42 ] ].
self assert: [ result class = Promise ].
result := Promise any: #( promise1 promise2 )
then: [ :value | self assert: [ value = 42 ] ].
self assert: [ result class = Promise ].
result := Promise race: #( promise1 promise2 )
then: [ :value | self assert: [ value = 42 ] ].
self assert: [ result class = Promise ].
!
async testAsyncAwait
| result |
result := await self asyncMethod.
self assert: [ result = 'asyncMethodResult' ].
!
async asyncMethod
"Await here shows the intended use,
but it does not do anything on constants."
^ await 'asyncMethodResult'.
!
testThenFinally
| promise1 promise2 |
promise1 := Promise resolve: 'resolved'.
promise2 := Promise fromJs: promise1 js
then: [ :result | self onThen: result ]
catch: [ :result | self onCatch: result ]
finally: [ self onFinally ].
!
onThen: result
self assert: [ result = 'resolved' ].
!
onCatch: error
"Error handling here shows the intended use,
but this method should not be reached."
error throw.
!
onFinally
"This method should be reached."
self assert: [ true ].
!
testCatch
| promise1 promise2 |
"This test is disabled by default because it halts the VSCode debugger.
It can be enabled to check that 'onExpectedCatch:' is called after continuing with [F5]."
^ self.
promise1 := Promise reject: 'rejected'.
promise2 := Promise fromJs: promise1 js
then: [ :result | self onThen: result ]
catch: [ :result | self onExpectedCatch: result ]
finally: [ self onFinally ].
!
onExpectedCatch: reason
self assert: [ reason = 'rejected' ].
!
CLASS TestTimer EXTENDS Test MODULE TestCore CLASSVARS '' VARS 'count'
testTimeout
| timer |
timer := Timer timeout: 10 then: [ self onTimeout: timer ].
!
onTimeout: timer
self assert: [ timer id > 0 ].
!
async testAwaitTimeout
| timer |
timer := Timer new.
await timer timeout: 10.
self assert: [ timer id > 0 ].
await Timer timeout: 10.
!
testClearTimeout
| timer |
timer := Timer timeout: 10 then: [ self notReached ].
timer clearTimeout.
!
notReached
self assert: [ false ].
!
testInterval
| timer |
count := 0.
timer := Timer interval: 10 then: [ self onInterval: timer ].
!
onInterval: timer
self assert: [ timer id > 0 ].
self assert: [ count < 2 ].
count increment >= 2 ifTrue: [
timer clearInterval ].
!
CLASS TestScreen EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
METHODS
test
| screen |
screen := Window default screen.
self assert: [ screen class = Screen ].
self assert: [ screen availTop >= 0 ].
self assert: [ screen availLeft >= 0 ].
self assert: [ screen height > 0 ].
self assert: [ screen width > 0 ].
self assert: [ screen availHeight > 0 ].
self assert: [ screen availWidth > 0 ].
self assert: [ screen colorDepth > 0 ].
self assert: [ screen pixelDepth > 0 ].
self assert: [ screen orientation class = ScreenOrientation ].
!
CLASS TestScreenOrientation EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
METHODS
test
| screenOrientation |
screenOrientation := Window default screen orientation.
self assert: [ screenOrientation class = ScreenOrientation ].
self assert: [
#( 'portrait-primary' 'portrait-secondary' 'landscape-primary' 'landscape-secondary' )
includes: screenOrientation type ].
self assert: [ screenOrientation angle >= 0 ].
screenOrientation lock: 'any' onLocked: [] onError: [].
screenOrientation unlock.
!
CLASS TestVisualViewport EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
METHODS
test
| visualViewport |
visualViewport := Window default visualViewport.
self assert: [ visualViewport class = VisualViewport ].
self assert: [ visualViewport offset >= ( 0 @ 0 ) ].
self assert: [ visualViewport pageOffset >= ( 0 @ 0 ) ].
self assert: [ visualViewport size >= ( 0 @ 0 ) ].
self assert: [ visualViewport scale > 0 ].
!
CLASS TestWindow EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
METHODS
testPlatforms
self assert: [ Window isFirefox | true ].
self assert: [ Window isChromium | true ].
self assert: [ Window isMacos | true ].
!
testProperties
| window |
window := Window default.
self assert:[ window class = Window ].
self assert:[ window closed not ].
self assert:[ window console = Console ].
self assert:[ window customElements class = CustomElementRegistry ].
self assert:[ window devicePixelRatio > 0.0 ].
self assert:[ window document = Document default ].
self assert:[ window frameElement isNil ].
self assert:[ window frames class = Window ].
self assert:[ window history class = History ].
self assert:[ window innerHeight > 0 ].
self assert:[ window innerWidth > 0 ].
self assert:[ window length >= 0 ].
self assert:[ window localStorage class = Storage ].
self assert:[ window location class = Location ].
self assert:[ window locationBarVisible ].
self assert:[ window menuBarVisible ].
self assert:[ window navigator class = Navigator ].
self assert:[ window opener isNil ].
self assert:[ window outerHeight > 0 ].
self assert:[ window outerWidth > 0 ].
self assert:[ window pageXOffset >= 0 ].
self assert:[ window pageYOffset >= 0 ].
self assert:[ window parent = window ].
self assert:[ window personalBarVisible ].
self assert:[ window screen class = Screen ].
self assert:[ window scrollBarsVisible | true ].
self assert:[ window scrollX >= 0 ].
self assert:[ window scrollY >= 0 ].
self assert:[ window self class = Window ].
self assert:[ window sessionStorage class = Storage ].
self assert:[ window speechSynthesis class = SpeechSynthesis ].
self assert:[ window statusBarVisible | true ].
self assert:[ window toolBarVisible | true ].
self assert:[ window top = window ].
self assert:[ window visualViewport class = VisualViewport ].
self assert:[ window window = window ].
!
CLASS TestHtmlTableElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
"This class tests TestHtmlTableElement and all elements contained within it."
METHODS
testTable
| table tHead tBody tFoot |
table := Document default createElement: 'table'.
self assert: [ table class = HtmlTableElement ].
tHead := table createTHead.
self assert: [ tHead class = HtmlTableSectionElement ].
self assert: [ table tHead = tHead ].
table deleteTHead.
self assert: [ table tHead isNil ].
tBody := table createTBody.
self assert: [ tBody class = HtmlTableSectionElement ].
self assert: [ table tBodies first = tBody ].
tFoot := table createTFoot.
self assert: [ tFoot class = HtmlTableSectionElement ].
self assert: [ table tFoot = tFoot ].
table deleteTFoot.
self assert: [ table tFoot isNil ].
!
testCaption
| table caption |
table := Document default createElement: 'table'.
self assert: [ table class = HtmlTableElement ].
caption := table createCaption.
self assert: [ caption class = HtmlTableCaptionElement ].
self assert: [ table caption = caption ].
caption innerHtml: 'My table caption'.
self assert: [ caption innerHtml = 'My table caption' ].
table deleteCaption.
self assert: [ table caption isNil ].
!
testSections
| table row tBody |
table := Document default createElement: 'table'.
self assert: [ table class = HtmlTableElement ].
tBody := table createTBody.
self assert: [ tBody class = HtmlTableSectionElement ].
row := table insertRow: -1.
self assert: [ row class = HtmlTableRowElement ].
self assert: [ tBody rows length = 1 ].
table deleteRow: 0.
self assert: [ tBody rows length = 0 ].
row := tBody insertRow: -1.
self assert: [ row class = HtmlTableRowElement ].
self assert: [ tBody rows length = 1 ].
tBody deleteRow: 0.
self assert: [ tBody rows length = 0 ].
!
testRow
| table row cell |
table := Document default createElement: 'table'.
self assert: [ table class = HtmlTableElement ].
row := table insertRow: -1.
self assert: [ row class = HtmlTableRowElement ].
self assert: [ row rowIndex = 0 ].
self assert: [ row sectionRowIndex = 0 ].
cell := row insertCell: -1.
self assert: [ row cells length = 1 ].
self assert: [ row cells first = cell ].
row deleteCell: 0.
self assert: [ row cells length = 0 ].
!
testCell
| table row headerCell cell |
table := Document default createElement: 'table'.
self assert: [ table class = HtmlTableElement ].
row := table insertRow: -1.
self assert: [ row class = HtmlTableRowElement ].
headerCell := Document default createElement: 'th'.
self assert: [ headerCell class = HtmlTableCellElement ].
headerCell abbr: 'myAbbr'.
self assert: [ headerCell abbr = 'myAbbr' ].
headerCell scope: 'row'.
self assert: [ headerCell scope = 'row' ].
headerCell id: 'headerCellId'.
row appendChild: headerCell.
self assert: [ headerCell cellIndex = 0 ].
cell := row insertCell: -1.
self assert: [ cell class = HtmlTableCellElement ].
self assert: [ cell cellIndex = 1 ].
cell headers: 'headerCellId'.
self assert: [ cell headers = 'headerCellId' ].
self assert: [ cell rowSpan = 1 ].
cell rowSpan: 2.
self assert: [ cell rowSpan = 2 ].
self assert: [ cell colSpan = 1 ].
cell colSpan: 2.
self assert: [ cell colSpan = 2 ].
!
CLASS TestAttr EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
METHODS
test
| attr |
"Attributes names should be lower case."
attr := Document new createAttribute: 'my-name'.
self assert: [ attr class = Attr ].
self assert: [ attr name = 'my-name' ].
attr value: 'myValue'.
self assert: [ attr value = 'myValue' ].
!
CLASS TestCharacterData EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
"Note: CharacterData is an abstract class.
We will test using instances of the Text subsclass."
METHODS
testProperties
| text |
text := Document default createTextNode: 'My Text'.
self assert: [ text data = 'My Text' ].
text data: text data, '!'.
self assert: [ text data = 'My Text!' ].
self assert: [ text length = 8 ].
self assert: [ text nextElementSibling = nil ].
self assert: [ text previousElementSibling = nil ].
!
testNodeMethods
| paragraph text |
paragraph := ( Document default createElement: 'p' ) textContent: 'My Paragraph'.
text := paragraph firstChild.
text before: '>'.
self assert: [ paragraph firstChild data = '>' ].
text after: '<'.
self assert: [ paragraph lastChild data = '<' ].
self assert: [ paragraph childNodes length = 3 ].
paragraph lastChild replaceWith: '= 1 ].
self assert: [ navigator language includes: '-' ].
self assert: [ navigator languages length >= 1 ].
self assert: [ navigator locks class = LockManager ].
self assert: [ navigator maxTouchPoints >= 0 ].
self assert: [ navigator mediaCapabilities class = MediaCapabilities ].
self assert: [ navigator mediaDevices class = MediaDevices ].
self assert: [ navigator mediaSession class = MediaSession ].
self assert: [ navigator onLine class = Boolean ].
self assert: [ navigator pdfViewerEnabled class = Boolean ].
self assert: [ navigator platform class = String ].
self assert: [ navigator permissions class = Permissions ].
self assert: [ navigator serviceWorker class = ServiceWorkerContainer ].
self assert: [ navigator storage class = StorageManager ].
self assert: [ navigator userAgent includes: 'Mozilla' ].
"2024-05-18: This currently results in error 405: Method not allowed"
"self assert: [ navigator sendBeacon: '/' data: 'ping' ]."
!
" TODO:
vibrate: pattern
^ Boolean fromJs: INLINE 'this.js.vibrate( pattern.$toJs() )'.
!
"CLASS TestElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
"Test Element class though div and paragraph subclass instances."
testProperties
| document div p1 p2 |
document := Document new.
div := document createElement: 'div'.
self assert: [ div class = HtmlDivElement ].
document body appendChild: div.
self assert: [ div isConnected ].
p1 := document createElement: 'p'.
self assert: [ p1 class = HtmlParagraphElement ].
div appendChild: p1.
p2 := document createElement: 'p'.
self assert: [ p2 class = HtmlParagraphElement ].
div appendChild: p2.
self assert: [ div id = '' ].
div id: 'div'.
self assert: [ div id = 'div' ].
self assert: [ ( div attributes getNamedItem: 'id' ) value = 'div' ].
self assert: [ div childElementCount = 2 ].
self assert: [ div children first = p1 ].
self assert: [ div className = '' ].
div className: 'divClass'.
self assert: [ div className = 'divClass' ].
self assert: [ div classList length = 1 ].
self assert: [ div classList contains: 'divClass' ].
self assert: [ div clientTop = 0 ].
self assert: [ div clientLeft = 0 ].
self assert: [ div clientHeight = 0 ].
self assert: [ div clientWidth = 0 ].
self assert: [ div firstElementChild = p1 ].
self assert: [ div lastElementChild = p2 ].
self assert: [ div innerHtml = '
' ].
self assert: [ div localName = 'div' ].
self assert: [ div namespaceUri startsWith: 'http' ].
self assert: [ div outerHtml startsWith: '' position: 'afterend'.
self assert: [ div lastElementChild id = 'p3' ].
div lastElementChild remove.
p2 insertAdjacentText: 'Inserted Text node' position: 'afterend'.
self assert: [ p2 nextSibling textContent = 'Inserted Text node' ].
p2 nextSibling remove.
self assert: [ ( p1 closest: 'div' ) id = 'div' ].
self assert: [ ( p1 getAttribute: 'id' ) = 'p1' ].
self assert: [ p1 getAttributeNames first = 'id' ].
self assert: [ ( p1 getAttributeNode: 'id' ) value = 'p1' ].
self assert: [ p1 hasAttribute: 'id' ].
self assert: [ p1 hasAttributes ].
self assert: [ p1 matches: '#p1' ].
rect0 := Rect origin: ( 0 @ 0 ) extent: ( 0 @ 0 ).
self assert: [ p1 getBoundingClientRect = rect0 ].
self assert: [ div getClientRects isEmpty ].
p1 className: 'pClass'.
self assert: [ ( document getElementsByClassName: 'pClass' ) first = p1 ].
self assert: [ ( document getElementsByTagName: 'p' ) first = p1 ].
!
testMethods2
| document div p1 p2 p3 p4 attr element paragraph text elements |
document := Document new.
div := ( document createElement: 'div' ) id: 'div'.
self assert: [ div class = HtmlDivElement ].
document body appendChild: div.
self assert: [ div isConnected ].
p1 := ( document createElement: 'p' ) id: 'p1'.
self assert: [ p1 class = HtmlParagraphElement ].
div appendChild: p1.
p2 := ( document createElement: 'p' ) id: 'p2'.
self assert: [ p1 class = HtmlParagraphElement ].
div appendChild: p2.
div prepend: p2.
self assert: [ div children first = p2 ].
div prepend: p1.
self assert: [ div children first = p1 ].
self assert: [ ( div querySelector: '#p1' ) id = 'p1' ].
self assert: [ ( div querySelectorAll: '#p1' ) first id = 'p1' ].
"Note: Attribute names must be lower case."
div setAttribute: 'my-attr' value: 'my-value'.
self assert: [ ( div getAttribute: 'my-attr' ) = 'my-value' ].
div removeAttribute: 'my-attr'.
self assert: [ ( div getAttribute: 'my-attr' ) = nil ].
attr := ( document createAttribute: 'my-attr2' ) value: 'my-value2'.
div setAttributeNode: attr.
self assert: [ ( div getAttribute: 'my-attr2' ) = 'my-value2' ].
div removeAttributeNode: attr.
self assert: [ ( div getAttribute: 'my-attr2' ) = nil ].
p3 := ( document createElement: 'p' ) id: 'p3'.
div replaceChildren: p3.
self assert: [ div children length = 1 ].
p4 := ( document createElement: 'p' ) id: 'p4'.
p3 replaceWith: p4.
self assert: [ div children first id = 'p4' ].
div removeChildren.
self assert: [ div children length = 0 ].
!
testCreation
| document element |
document := Document new.
#( 'body' 'br' 'button' 'div' 'embed' 'field' 'form' 'head' 'html' 'image' 'input'
'label' 'link' 'meta' 'p' 'script' 'slot' 'span' 'textarea' 'title' 'unknown' )
do: [ :tagName |
element := document createElement: tagName.
self assert: [ element tagName = tagName toUpperCase ] ].
!
CLASS TestHtmlAnchorElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
test
| anchor |
anchor := Document new createElement: 'a'.
self assert: [ anchor class = HtmlAnchorElement ].
self assert: [ anchor download = '' ].
anchor download: 'Test.txt'.
self assert: [ anchor download = 'Test.txt' ].
self assert: [ anchor href = '' ].
self assert: [ anchor protocol = ':' ].
self assert: [ anchor host = '' ].
self assert: [ anchor port = '' ].
self assert: [ anchor hostname = '' ].
self assert: [ anchor origin = '' ].
anchor href: 'http://localhost:8080/SmallJS.png'.
self assert: [ anchor href = 'http://localhost:8080/SmallJS.png' ].
self assert: [ anchor host = 'localhost:8080' ].
self assert: [ anchor hostname = 'localhost' ].
self assert: [ anchor port = '8080' ].
self assert: [ anchor pathname = '/SmallJS.png' ].
self assert: [ anchor origin = 'http://localhost:8080' ].
self assert: [ anchor hreflang = '' ].
anchor hreflang: 'en'.
self assert: [ anchor hreflang = 'en' ].
self assert: [ anchor username = '' ].
anchor username: 'John'.
self assert: [ anchor username = 'John' ].
self assert: [ anchor password = '' ].
anchor password: 'secret'.
self assert: [ anchor password = 'secret' ].
self assert: [ anchor protocol = 'http:' ].
anchor protocol: 'ftp:'.
self assert: [ anchor protocol = 'ftp:' ].
self assert: [ anchor referrerPolicy = '' ].
anchor referrerPolicy: 'origin'.
self assert: [ anchor referrerPolicy = 'origin' ].
self assert: [ anchor rel = '' ].
anchor rel: 'alternate'.
self assert: [ anchor rel = 'alternate' ].
self assert: [ anchor relList length = 1 ].
self assert: [ anchor search = '' ].
anchor search: '?q=123'.
self assert: [ anchor search = '?q=123' ].
self assert: [ anchor target = '' ].
anchor target: '_blank'.
self assert: [ anchor target = '_blank' ].
self assert: [ anchor text = '' ].
anchor text: 'SmallJS.png'.
self assert: [ anchor text = 'SmallJS.png' ].
self assert: [ anchor type = '' ].
anchor type: 'image/jpg'.
self assert: [ anchor type = 'image/jpg' ].
!
CLASS TestHtmlButtonElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
"Form related properties of buttons are tested in TestHtmlFormElement."
test
| button |
button := Document new createElement: 'button'.
self assert: [ button class = HtmlButtonElement ].
self assert: [ button autofocus not ].
button autofocus: true.
self assert: [ button autofocus ].
self assert: [ button disabled not ].
button disabled: true.
self assert: [ button disabled ].
self assert: [ button labels isEmpty ].
self assert: [ button name = '' ].
button name: 'buttonName'.
self assert: [ button name= 'buttonName' ].
self assert: [ button tabIndex = 0 ].
button tabIndex: -1.
self assert: [ button tabIndex = -1 ].
self assert: [ button type = 'submit' ].
button type: 'button'.
self assert: [ button type = 'button' ].
self assert: [ button willValidate not ].
self assert: [ button validationMessage = '' ].
self assert: [ button valid ].
self assert: [ button value = '' ].
!
CLASS TestHtmlDataListElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
test
| document dataList option options |
document := Document new.
dataList := document createElement: 'datalist'.
self assert: [ dataList class = HtmlDataListElement ].
#( 'First' 'Second' ) do: [ :optionString |
option := document createElement: 'option'.
self assert: [ option class = HtmlOptionElement ].
option value: optionString.
dataList appendChild: option ].
options := dataList options.
self assert: [ options length = 2 ].
self assert: [ options first value = 'First' ].
self assert: [ ( dataList indexOf: 'Second' ) = 1 ].
self assert: [ ( dataList indexOf: 'Missing' ) = -1 ].
!
CLASS TestHtmlElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
"Test HtmlElement through instances of subclasses."
testProperties
| paragraph |
paragraph := Document new createElement: 'p'.
self assert: [ paragraph class = HtmlParagraphElement ].
self assert: [ paragraph accessKey = '' ].
paragraph accessKey: 'q'.
self assert: [ paragraph accessKey = 'q' ].
self assert: [ paragraph dir = '' ].
paragraph dir: 'ltr'.
self assert: [ paragraph dir = 'ltr' ].
self assert: [ paragraph enterKeyHint = '' ].
paragraph enterKeyHint: 'done'.
self assert: [ paragraph enterKeyHint = 'done' ].
self assert: [ paragraph hidden not ].
paragraph hidden: true.
self assert: [ paragraph hidden ].
self assert: [ paragraph inert not ].
paragraph inert: true.
self assert: [ paragraph inert ].
self assert: [ paragraph innerText = '' ].
paragraph innerText: 'Text'.
self assert: [ paragraph innerText = 'Text' ].
self assert: [ paragraph outerText = paragraph innerText ].
self assert: [ paragraph isContentEditable not ].
self assert: [ paragraph lang = '' ].
paragraph lang: 'en'.
self assert: [ paragraph lang = 'en' ].
self assert: [ paragraph nonce = '' ].
paragraph nonce: 'whatever'.
self assert: [ paragraph nonce = 'whatever' ].
paragraph nonce: ''.
self assert: [ paragraph offsetTop = 0 ].
self assert: [ paragraph offsetLeft = 0 ].
self assert: [ paragraph offsetHeight = 0 ].
self assert: [ paragraph offsetWidth = 0 ].
self assert: [ paragraph style class = CssStyleDeclaration ].
self assert: [ paragraph tabIndex = -1 ].
paragraph tabIndex: 0.
self assert: [ paragraph tabIndex = 0 ].
paragraph tabIndex: -1.
self assert: [ paragraph title = '' ].
paragraph title: 'Title'.
self assert: [ paragraph title = 'Title' ].
!
testMethods
| document paragraph |
document := Document new.
"These can only be tested in visible documents:
paragraph forceFocus.
paragraph blur."
"Method click is tested in TestEventTarget."
!
CLASS TestHtmlEmbedElement EXTENDS Test MODULE TestBrowser CLASSVARS '' VARS ''
testProperties
| body embed |
body := Document new body
innerHtml: '