Complete legacy operator UI and playout migration
This commit is contained in:
@@ -45,9 +45,33 @@ $node = Resolve-NodePath -ExplicitPath $NodePath
|
||||
$repositoryRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$scripts = @(
|
||||
(Join-Path $repositoryRoot 'Web\playout-safety.js'),
|
||||
(Join-Path $repositoryRoot 'Web\operator-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\legacy-fixed-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\industry-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\comparison-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\theme-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\overseas-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\expert-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\trading-halt-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\candle-options-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\manual-lists-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\manual-financial-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\named-manual-restore-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\operator-catalog-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\manual-lists-ui.js'),
|
||||
(Join-Path $repositoryRoot 'Web\manual-financial-ui.js'),
|
||||
(Join-Path $repositoryRoot 'Web\operator-catalog-ui.js'),
|
||||
(Join-Path $repositoryRoot 'Web\named-playlist-workflow.js'),
|
||||
(Join-Path $repositoryRoot 'Web\app.js')
|
||||
)
|
||||
$test = Join-Path $repositoryRoot 'tests\Web\playout-safety.test.cjs'
|
||||
$testFiles = @(
|
||||
Get-ChildItem -LiteralPath (Join-Path $repositoryRoot 'tests\Web') -Filter '*.test.cjs' -File |
|
||||
Sort-Object -Property Name |
|
||||
Select-Object -ExpandProperty FullName
|
||||
)
|
||||
if ($testFiles.Count -eq 0) {
|
||||
throw 'No Web playout test files were found.'
|
||||
}
|
||||
|
||||
foreach ($script in $scripts) {
|
||||
& $node --check $script
|
||||
@@ -57,7 +81,14 @@ foreach ($script in $scripts) {
|
||||
}
|
||||
|
||||
$appScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'Web\app.js') -Raw -Encoding UTF8
|
||||
$indexMarkup = Get-Content -LiteralPath (Join-Path $repositoryRoot 'Web\index.html') -Raw -Encoding UTF8
|
||||
$namedPlaylistScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'Web\named-playlist-workflow.js') -Raw -Encoding UTF8
|
||||
$mainWindowScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'MainWindow.xaml.cs') -Raw -Encoding UTF8
|
||||
$namedPlaylistBridgeScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'MainWindow.NamedPlaylists.cs') -Raw -Encoding UTF8
|
||||
$pagePlanBridgeScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'MainWindow.PlaylistPagePlans.cs') -Raw -Encoding UTF8
|
||||
$legacyWorkflowScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'src\MBN_STOCK_WEBVIEW.Core\Playout\Scenes\LegacyPlayoutWorkflow.cs') -Raw -Encoding UTF8
|
||||
$backgroundScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'MainWindow.Background.cs') -Raw -Encoding UTF8
|
||||
$playoutMainScript = Get-Content -LiteralPath (Join-Path $repositoryRoot 'MainWindow.Playout.cs') -Raw -Encoding UTF8
|
||||
$sceneCatalogMatches = [regex]::Matches(
|
||||
$appScript,
|
||||
'builderKey:\s*"s[0-9]+"')
|
||||
@@ -91,17 +122,186 @@ if ($appScript -notmatch 'normalizeScenePreviewFields' -or
|
||||
$appScript -notmatch 'renderSceneDataPreview') {
|
||||
throw 'Web playout must render bounded authoritative scene data and complete page state.'
|
||||
}
|
||||
if ($appScript -notmatch 'row-enabled' -or
|
||||
$appScript -notmatch 'item\.enabled = include\.checked' -or
|
||||
if ($appScript -notmatch 'checkbox\.checked = item\.enabled !== false' -or
|
||||
$appScript -notmatch 'item\.enabled = checkbox\.checked' -or
|
||||
$appScript -notmatch 'isPlaylistSnapshotLocked' -or
|
||||
$appScript -notmatch 'pending: Boolean\(state\.playout\.pending\)' -or
|
||||
$appScript -notmatch 'outcomeUnknown: state\.playout\.sessionQuarantined') {
|
||||
throw 'Web playlist must expose the legacy active flag and freeze its native snapshot.'
|
||||
}
|
||||
if ($appScript -notmatch 'toggleAllEntriesEnabled' -or
|
||||
$appScript -notmatch 'deleteAllPlaylistEntries' -or
|
||||
$appScript -notmatch 'event\?\.shiftKey' -or
|
||||
$appScript -notmatch 'event\.key === "Home"' -or
|
||||
$appScript -notmatch 'event\.key === "End"') {
|
||||
throw 'Legacy playlist enabled-all, delete-all, range, Home and End controls are incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'postNative\("playout-timeout-quarantine", \{ requestId, command \}\)' -or
|
||||
$appScript -notmatch 'browserCorrelationQuarantined') {
|
||||
throw 'Web response timeouts must latch the process-lifetime native quarantine.'
|
||||
}
|
||||
if ($appScript -match 'event\.key === "F2"\) requestPlayoutCommand' -or
|
||||
$appScript -notmatch 'notifyLegacyBackgroundShortcut' -or
|
||||
$appScript -notmatch 'legacyTakeInShortcutAction' -or
|
||||
$appScript -notmatch 'synchronizeStockCandleOptions' -or
|
||||
$appScript -notmatch 'operatorInputBuilderKeys\.has\(definition\.builderKey\)') {
|
||||
throw 'Legacy F2, F8, moving-average and typed-playlist safety integration is incomplete.'
|
||||
}
|
||||
if ($indexMarkup -notmatch 'id="globalMa5"' -or
|
||||
$indexMarkup -notmatch 'id="globalMa20"' -or
|
||||
$indexMarkup -notmatch '<script src="candle-options-workflow\.js"></script>' -or
|
||||
$appScript -notmatch 'MbnCandleOptionsWorkflow' -or
|
||||
$appScript -notmatch 'synchronizeGlobalCandleOptions' -or
|
||||
$appScript -notmatch 'legacy-overseas-workflow' -or
|
||||
$appScript -notmatch 'legacy-trading-halt-workflow') {
|
||||
throw 'The original global 5-day/20-day candle options are not synchronized across every candle workflow.'
|
||||
}
|
||||
foreach ($asset in @(
|
||||
'manual-lists-workflow.js', 'manual-financial-workflow.js', 'named-manual-restore-workflow.js', 'operator-catalog-workflow.js',
|
||||
'manual-lists-ui.js', 'manual-financial-ui.js', 'operator-catalog-ui.js')) {
|
||||
if ($indexMarkup -notmatch [regex]::Escape("<script src=`"$asset`"></script>")) {
|
||||
throw "Operator UI script is not loaded: $asset"
|
||||
}
|
||||
}
|
||||
foreach ($asset in @('manual-lists-ui.css', 'manual-financial-ui.css', 'operator-catalog-ui.css')) {
|
||||
if ($indexMarkup -notmatch [regex]::Escape("<link rel=`"stylesheet`" href=`"$asset`">") ) {
|
||||
throw "Operator UI stylesheet is not loaded: $asset"
|
||||
}
|
||||
}
|
||||
if ($indexMarkup -notmatch 'id="themeCatalogManageButton"' -or
|
||||
$indexMarkup -notmatch 'id="expertCatalogManageButton"' -or
|
||||
$appScript -notmatch 'MbnManualListsUi' -or
|
||||
$appScript -notmatch 'MbnManualFinancialUi' -or
|
||||
$appScript -notmatch 'MbnOperatorCatalogUi' -or
|
||||
$appScript -notmatch 'initializeOperatorUiControllers' -or
|
||||
$appScript -notmatch 'appendTrustedOperatorEntry' -or
|
||||
$appScript -notmatch 'manualFinancialPrepareBlocked' -or
|
||||
$appScript -notmatch 'createRestoreSelectionRequest' -or
|
||||
$appScript -notmatch 'operatorCatalogUi\?\.loseCorrelation') {
|
||||
throw 'GraphE, FSell, VIList, ThemeA or EList is not fully connected to the Web operator surface.'
|
||||
}
|
||||
if ($appScript -notmatch 'requestBackgroundSelection' -or
|
||||
$appScript -notmatch 'event\.key === "F3"' -or
|
||||
$appScript -notmatch 'postNative\("request-background-status"' -or
|
||||
$mainWindowScript -notmatch 'case "choose-background"' -or
|
||||
$mainWindowScript -notmatch 'case "toggle-background"' -or
|
||||
$playoutMainScript -notmatch 'MutableLegacySceneCueCompositionOptionsSource' -or
|
||||
$backgroundScript -notmatch 'OperatorBackgroundExtensions') {
|
||||
throw 'Legacy F2/F3 native background selection integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreFixedPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeFixedEntries' -or
|
||||
$appScript -notmatch 'renderFixedWorkflow' -or
|
||||
$appScript -notmatch 'legacy-fixed-workflow') {
|
||||
throw 'Legacy fixed operator catalog integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreIndustryPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeIndustryEntries' -or
|
||||
$appScript -notmatch 'renderIndustryWorkflow' -or
|
||||
$appScript -notmatch 'postNative\("request-industries", \{ requestId, market \}\)' -or
|
||||
$mainWindowScript -notmatch 'case "request-industries"' -or
|
||||
$mainWindowScript -notmatch 'PostMessage\("industry-results"') {
|
||||
throw 'Legacy KOSPI/KOSDAQ industry selector integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreComparisonPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeComparisonEntries' -or
|
||||
$appScript -notmatch 'renderComparisonWorkflow' -or
|
||||
$appScript -notmatch 'postNative\("search-world-stocks"' -or
|
||||
$mainWindowScript -notmatch 'case "search-world-stocks"' -or
|
||||
$mainWindowScript -notmatch 'PostMessage\("world-stock-search-results"') {
|
||||
throw 'Legacy comparison selector and world-stock search integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreThemePlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeThemeEntries' -or
|
||||
$appScript -notmatch 'renderThemeWorkflow' -or
|
||||
$appScript -notmatch 'postNative\("search-themes"' -or
|
||||
$appScript -notmatch 'postNative\("request-theme-preview"' -or
|
||||
$mainWindowScript -notmatch 'case "search-themes"' -or
|
||||
$mainWindowScript -notmatch 'case "request-theme-preview"') {
|
||||
throw 'Legacy read-only theme selector integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreOverseasPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeOverseasEntries' -or
|
||||
$appScript -notmatch 'renderOverseasWorkflow' -or
|
||||
$appScript -notmatch 'postNative\(overseasWorkflow\.bridgeContracts\[kind\]\.requestType, request\)' -or
|
||||
$mainWindowScript -notmatch 'case "search-overseas-industries"' -or
|
||||
$mainWindowScript -notmatch 'PostMessage\("overseas-industry-results"') {
|
||||
throw 'Legacy overseas-industry and overseas-stock selector integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreExpertPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeExpertEntries' -or
|
||||
$appScript -notmatch 'renderExpertWorkflow' -or
|
||||
$appScript -notmatch 'postNative\(expertWorkflow\.bridgeContract\.previewRequestType, request\)' -or
|
||||
$mainWindowScript -notmatch 'case "search-experts"' -or
|
||||
$mainWindowScript -notmatch 'case "request-expert-preview"') {
|
||||
throw 'Legacy read-only expert selector integration is incomplete.'
|
||||
}
|
||||
if ($appScript -notmatch 'restoreTradingHaltPlaylistEntry' -or
|
||||
$appScript -notmatch 'synchronizeTradingHaltEntries' -or
|
||||
$appScript -notmatch 'renderTradingHaltWorkflow' -or
|
||||
$appScript -notmatch 'postNative\(tradingHaltWorkflow\.bridgeContract\.requestType, request\)' -or
|
||||
$mainWindowScript -notmatch 'case "search-trading-halts"' -or
|
||||
$mainWindowScript -notmatch 'PostMessage\("trading-halt-results"') {
|
||||
throw 'Legacy trading-halt selector integration is incomplete.'
|
||||
}
|
||||
if ($indexMarkup -notmatch 'id="namedPlaylistPanel"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistDefinitions"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistCreateButton"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistReplaceButton"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistDeleteButton"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistPageRetryButton"' -or
|
||||
$indexMarkup -notmatch '<script src="named-playlist-workflow\.js"></script>' -or
|
||||
$indexMarkup -notmatch 'id="loadButton"' -or
|
||||
$indexMarkup -notmatch 'id="saveButton"' -or
|
||||
$indexMarkup -notmatch 'id="namedPlaylistOpenButton"') {
|
||||
throw 'Named Oracle playlist controls must remain explicit and separate from local temporary storage.'
|
||||
}
|
||||
if ($appScript -notmatch 'MbnNamedPlaylistWorkflow' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistList' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistLoad' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistCreate' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistReplace' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistDelete' -or
|
||||
$appScript -notmatch 'namedPlaylistPrepareBlockers' -or
|
||||
$appScript -notmatch 'restoreRawRows\(load, resolveNamedPlaylistRawRow\)' -or
|
||||
$appScript -notmatch 'requestNamedPlaylistPagePlans' -or
|
||||
$appScript -notmatch 'normalizePagePlanResponse\(payload, pending\.request\)' -or
|
||||
$appScript -notmatch 'page-result-empty' -or
|
||||
$appScript -notmatch 'namedPlaylistBackupStorageKey' -or
|
||||
$appScript -notmatch 'writeQuarantined') {
|
||||
throw 'Named Oracle playlist UI, trusted restoration, backup, PREPARE guard, or write quarantine is incomplete.'
|
||||
}
|
||||
if ($namedPlaylistScript -notmatch 'request-playlist-page-plans' -or
|
||||
$namedPlaylistScript -notmatch 'playlist-page-plans-results' -or
|
||||
$namedPlaylistScript -notmatch 'playlist-page-plans-error' -or
|
||||
$mainWindowScript -notmatch 'case "request-playlist-page-plans"' -or
|
||||
$pagePlanBridgeScript -notmatch 'PreflightPagePlansAsync' -or
|
||||
$pagePlanBridgeScript -notmatch 'await _databaseActivityGate\.WaitAsync\(requestToken\)' -or
|
||||
$pagePlanBridgeScript -notmatch 'Volatile\.Read\(ref _playoutCommandInFlight\) != 0' -or
|
||||
$legacyWorkflowScript -notmatch 'PreflightPagePlansAsync') {
|
||||
throw 'Read-only named-playlist page preflight, exact Web correlation, or playout-priority gating is incomplete.'
|
||||
}
|
||||
foreach ($requestType in @(
|
||||
'request-named-playlist-list',
|
||||
'request-named-playlist-load',
|
||||
'create-named-playlist',
|
||||
'replace-named-playlist',
|
||||
'delete-named-playlist')) {
|
||||
if ($namedPlaylistScript -notmatch [regex]::Escape($requestType) -or
|
||||
$mainWindowScript -notmatch [regex]::Escape($requestType)) {
|
||||
throw "Named playlist request contract is incomplete: $requestType"
|
||||
}
|
||||
}
|
||||
foreach ($responseType in @(
|
||||
'named-playlist-list-results',
|
||||
'named-playlist-load-results',
|
||||
'named-playlist-mutation-results',
|
||||
'named-playlist-error')) {
|
||||
if ($appScript -notmatch [regex]::Escape($responseType) -or
|
||||
$namedPlaylistBridgeScript -notmatch [regex]::Escape($responseType)) {
|
||||
throw "Named playlist response contract is incomplete: $responseType"
|
||||
}
|
||||
}
|
||||
if ($mainWindowScript -notmatch 'OnNavigationStarting' -or
|
||||
$mainWindowScript -notmatch 'OnProcessFailed' -or
|
||||
$mainWindowScript -notmatch 'ReloadBrowserSafely' -or
|
||||
@@ -110,7 +310,7 @@ if ($mainWindowScript -notmatch 'OnNavigationStarting' -or
|
||||
throw 'Reload, navigation, and WebView process failure must quarantine in-flight playout correlation.'
|
||||
}
|
||||
|
||||
& $node --test $test
|
||||
& $node --test @testFiles
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Web playout tests failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
@@ -120,5 +320,5 @@ if ($LASTEXITCODE -ne 0) {
|
||||
SyntaxFiles = $scripts.Count
|
||||
SceneCatalogEntries = $sceneCatalogMatches.Count
|
||||
ReachableSelectionPresets = $selectionPresetMatches.Count
|
||||
TestFile = [IO.Path]::GetFileName($test)
|
||||
TestFiles = $testFiles.Count
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user