import 'package:flame_lua_runtime/runtime/models/runtime_node.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('RuntimeNode', () { test('parses required and optional fields', () { final node = RuntimeNode.fromMap({ 'id': 'dice_button', 'type': 'button', 'parent': 'top_bar', 'asset': 'dice_normal', 'frame': 'dice_idle.png', 'pressedFrame': 'dice_pressed.png', 'disabledFrame': 'dice_disabled.png', 'sourceX': 4, 'sourceY': 5, 'sourceWidth': 64, 'sourceHeight': 32, 'sliceLeft': 6, 'sliceTop': 7, 'sliceRight': 8, 'sliceBottom': 9, 'pressedAsset': 'dice_pressed', 'disabledAsset': 'dice_disabled', 'animation': 'idle', 'skin': 'red', 'loop': false, 'text': 'Roll', 'x': 10, 'y': 20.5, 'width': 120, 'height': 48, 'paddingLeft': 4, 'paddingTop': 5, 'paddingRight': 6, 'paddingBottom': 7, 'anchor': 'center', 'layer': 3, 'visible': false, 'alpha': 0.7, 'scale': 1.2, 'rotation': 0.4, 'color': '#112233', 'fontSize': 18, 'textAlign': 'left', 'textShadowColor': '#80000000', 'textShadowOffsetX': 2, 'textShadowOffsetY': 3, 'textShadowBlur': 4, 'radius': 10, 'strokeWidth': 3, 'value': 0.6, 'scrollX': 15, 'scrollY': 20, 'contentWidth': 220, 'contentHeight': 180, 'virtualized': true, 'cacheExtent': 12, 'inertia': false, 'scrollbarThumbColor': '#abcdef', 'scrollbarTrackColor': '#123456', 'scrollbarThickness': 6, 'scrollbarVisible': false, 'interactive': true, 'onTap': 'roll_dice', 'onScroll': 'list_scrolled', 'preset': 'burst', 'count': 32, 'duration': 0.6, 'speedMin': 60, 'speedMax': 180, 'gravityX': 0, 'gravityY': 120, 'spread': 360, 'colorTo': '#00ffcc33', 'radiusTo': 0, 'autoRemove': false, 'fadeOut': false, }); expect(node.id, 'dice_button'); expect(node.type, 'button'); expect(node.parent, 'top_bar'); expect(node.asset, 'dice_normal'); expect(node.frame, 'dice_idle.png'); expect(node.pressedFrame, 'dice_pressed.png'); expect(node.disabledFrame, 'dice_disabled.png'); expect(node.sourceX, 4); expect(node.sourceY, 5); expect(node.sourceWidth, 64); expect(node.sourceHeight, 32); expect(node.sliceLeft, 6); expect(node.sliceTop, 7); expect(node.sliceRight, 8); expect(node.sliceBottom, 9); expect(node.pressedAsset, 'dice_pressed'); expect(node.disabledAsset, 'dice_disabled'); expect(node.animation, 'idle'); expect(node.skin, 'red'); expect(node.loop, isFalse); expect(node.text, 'Roll'); expect(node.x, 10); expect(node.y, 20.5); expect(node.width, 120); expect(node.height, 48); expect(node.paddingLeft, 4); expect(node.paddingTop, 5); expect(node.paddingRight, 6); expect(node.paddingBottom, 7); expect(node.anchor, 'center'); expect(node.layer, 3); expect(node.visible, isFalse); expect(node.alpha, 0.7); expect(node.scale, 1.2); expect(node.rotation, 0.4); expect(node.color, const Color(0xff112233)); expect(node.fontSize, 18); expect(node.textAlign, 'left'); expect(node.textShadowColor, const Color(0x80000000)); expect(node.textShadowOffsetX, 2); expect(node.textShadowOffsetY, 3); expect(node.textShadowBlur, 4); expect(node.radius, 10); expect(node.strokeWidth, 3); expect(node.value, 0.6); expect(node.scrollX, 15); expect(node.scrollY, 20); expect(node.contentWidth, 220); expect(node.contentHeight, 180); expect(node.virtualized, isTrue); expect(node.cacheExtent, 12); expect(node.inertia, isFalse); expect(node.scrollbarThumbColor, const Color(0xffabcdef)); expect(node.scrollbarTrackColor, const Color(0xff123456)); expect(node.scrollbarThickness, 6); expect(node.scrollbarVisible, isFalse); expect(node.interactive, isTrue); expect(node.onTap, 'roll_dice'); expect(node.onScroll, 'list_scrolled'); expect(node.preset, 'burst'); expect(node.count, 32); expect(node.duration, 0.6); expect(node.speedMin, 60); expect(node.speedMax, 180); expect(node.gravityX, 0); expect(node.gravityY, 120); expect(node.spread, 360); expect(node.colorTo, const Color(0x00ffcc33)); expect(node.radiusTo, 0); expect(node.autoRemove, isFalse); expect(node.fadeOut, isFalse); }); test('applies default values', () { final node = RuntimeNode.fromMap({'id': 'label', 'type': 'text'}); expect(node.x, 0); expect(node.y, 0); expect(node.anchor, 'topLeft'); expect(node.layer, 0); expect(node.visible, isTrue); expect(node.alpha, 1); expect(node.scale, 1); expect(node.rotation, 0); expect(node.loop, isTrue); expect(node.textAlign, 'center'); expect(node.textShadowColor, isNull); expect(node.textShadowOffsetX, isNull); expect(node.textShadowOffsetY, isNull); expect(node.textShadowBlur, isNull); expect(node.frame, isNull); expect(node.pressedFrame, isNull); expect(node.disabledFrame, isNull); expect(node.sourceX, isNull); expect(node.sourceY, isNull); expect(node.sourceWidth, isNull); expect(node.sourceHeight, isNull); expect(node.sliceLeft, isNull); expect(node.sliceTop, isNull); expect(node.sliceRight, isNull); expect(node.sliceBottom, isNull); expect(node.scrollbarVisible, isTrue); expect(node.paddingLeft, 0); expect(node.paddingTop, 0); expect(node.paddingRight, 0); expect(node.paddingBottom, 0); expect(node.autoRemove, isTrue); expect(node.fadeOut, isTrue); expect(node.interactive, isFalse); }); test('copies only provided props', () { final node = RuntimeNode.fromMap({ 'id': 'piece', 'type': 'circle', 'x': 1, 'y': 2, 'color': '#ff0000', }); final updated = node.copyWithProps({ 'x': 20, 'parent': 'board', 'visible': false, 'color': '#8000ff00', 'radius': 8, 'strokeWidth': 2, 'value': 0.75, 'width': 70, 'height': 60, 'paddingLeft': 8, 'paddingTop': 9, 'paddingRight': 10, 'paddingBottom': 11, 'contentWidth': 120, 'contentHeight': 100, 'sourceX': 3, 'sourceY': 4, 'sourceWidth': 40, 'sourceHeight': 41, 'frame': 'piece.png', 'pressedFrame': 'piece_down.png', 'disabledFrame': 'piece_disabled.png', 'sliceLeft': 5, 'sliceTop': 6, 'sliceRight': 7, 'sliceBottom': 8, 'pressedAsset': 'button_pressed', 'disabledAsset': 'button_disabled', 'scrollX': 90, 'scrollY': 80, 'textAlign': 'right', 'textShadowColor': '#40000000', 'textShadowOffsetX': 1, 'textShadowOffsetY': 2, 'textShadowBlur': 3, 'preset': 'trail', 'count': 12, }); expect(updated.id, 'piece'); expect(updated.type, 'circle'); expect(updated.parent, 'board'); expect(updated.x, 20); expect(updated.y, 2); expect(updated.visible, isFalse); expect(updated.color, const Color(0x8000ff00)); expect(updated.radius, 8); expect(updated.strokeWidth, 2); expect(updated.value, 0.75); expect(updated.width, 70); expect(updated.height, 60); expect(updated.paddingLeft, 8); expect(updated.paddingTop, 9); expect(updated.paddingRight, 10); expect(updated.paddingBottom, 11); expect(updated.contentWidth, 120); expect(updated.contentHeight, 100); expect(updated.sourceX, 3); expect(updated.sourceY, 4); expect(updated.sourceWidth, 40); expect(updated.sourceHeight, 41); expect(updated.frame, 'piece.png'); expect(updated.pressedFrame, 'piece_down.png'); expect(updated.disabledFrame, 'piece_disabled.png'); expect(updated.sliceLeft, 5); expect(updated.sliceTop, 6); expect(updated.sliceRight, 7); expect(updated.sliceBottom, 8); expect(updated.pressedAsset, 'button_pressed'); expect(updated.disabledAsset, 'button_disabled'); expect(updated.scrollX, 68); expect(updated.scrollY, 60); expect(updated.textAlign, 'right'); expect(updated.textShadowColor, const Color(0x40000000)); expect(updated.textShadowOffsetX, 1); expect(updated.textShadowOffsetY, 2); expect(updated.textShadowBlur, 3); expect(updated.preset, 'trail'); expect(updated.count, 12); }); test('rejects invalid values', () { expect( () => RuntimeNode.fromMap({'id': '', 'type': 'text'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'text', 'x': 'bad'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'text', 'color': 'red'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'unknown'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'text', 'anchor': 'middle', }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'progress', 'value': 2}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'image', 'sourceWidth': 0}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'image', 'sliceLeft': -1}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'text', 'textAlign': 'justify', }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'text', 'textShadowBlur': -1, }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'listView', 'scrollY': -1}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'particle', 'preset': 'unknown', }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'particle', 'count': 0}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'listView', 'paddingTop': -1, }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'listView', 'cacheExtent': -1, }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'rect', 'interactive': 'yes', }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'button', 'pressedAsset': 1, }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'rect', 'parent': 1}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'spine', 'loop': 'yes'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({'id': 'a', 'type': 'rect', 'parent': 'a'}), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'rect', 'interative': true, }), throwsFormatException, ); expect( () => RuntimeNode.fromMap({ 'id': 'a', 'type': 'rect', }).copyWithProps({'screenWitdh': 720}), throwsFormatException, ); }); }); }