Java script alerts fix#4389
Conversation
WalkthroughThis PR introduces a comprehensive Ginger Execution Report system with static HTML templates, CSS/JavaScript assets, and enhancements to Excel action handling. New report templates support hierarchical views (RunSet, GingerRunner, BusinessFlow, Activity, ActivityGroup, Action), along with bundled libraries (jQuery, Bootstrap, Font Awesome) and chart rendering capabilities. The Excel action implementation is significantly refactored to support cell-address-based operations, new data selection modes, and direct cell writing via a new WriteCellData method. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 44
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Ginger/GingerCoreNET/Database/DatabaseOperations.cs (1)
473-490: MySQL port parsing assigns to wrong variable, breaking port handling.Line 478 assigns the parsed port to
portinstead ofport1. Sinceport1is never assigned, the condition at line 490 (port1.HasValue) will always be false, and custom MySQL ports will be silently ignored.Additionally,
port1is declared asuint?but the parsing usesint.TryParse.🔎 Proposed fix
case eDBTypes.MySQL: string mySQLHost = TNSCalculated; uint? port1 = null; if (TNSCalculated.Contains(':', StringComparison.Ordinal)) { var parts = TNSCalculated.Split(':', 2); mySQLHost = parts[0]; - if (int.TryParse(parts[1], out int p)) port = p; + if (uint.TryParse(parts[1], out uint p)) port1 = p; }
📜 Review details
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (16)
Ginger/Ginger/Reports/GingerExecutionReport/Images/GingerLogo.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/PreviewDummyReport/RunSet.zipis excluded by!**/*.zip,!**/*.zipGinger/Ginger/Reports/GingerExecutionReport/assets/fonts/SourceSansPro-Regular.ttfis excluded by!**/*.ttf,!**/*.ttfGinger/Ginger/Reports/GingerExecutionReport/assets/fonts/fontawesome-webfont.eotis excluded by!**/*.eot,!**/*.eotGinger/Ginger/Reports/GingerExecutionReport/assets/fonts/fontawesome-webfont.ttfis excluded by!**/*.ttf,!**/*.ttfGinger/Ginger/Reports/GingerExecutionReport/assets/img/@BeatLogo - Copy.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/@BeatLogo.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/@Ginger - Copy.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/@Ginger.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/@item_next.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/@item_prev.pngis excluded by!**/*.png,!**/*.pngGinger/Ginger/Reports/GingerExecutionReport/assets/img/comments-icon.jpgis excluded by!**/*.jpg,!**/*.jpgGinger/Ginger/Reports/GingerExecutionReport/assets/js/Recharts.min.jsis excluded by!**/*.min.js,!**/*.min.jsGinger/Ginger/Reports/GingerExecutionReport/assets/js/prop-types.min.jsis excluded by!**/*.min.js,!**/*.min.jsGinger/Ginger/Reports/GingerExecutionReport/assets/js/react-dom.min.jsis excluded by!**/*.min.js,!**/*.min.jsGinger/Ginger/Reports/GingerExecutionReport/assets/js/react-with-addons.min.jsis excluded by!**/*.min.js,!**/*.min.js
📒 Files selected for processing (18)
Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/ActivityGroupReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/ActivityReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/BusinessFlowReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/GingerExecutionReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/GingerRunnerReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLDetailedReport.cs(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.cs(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLSummaryReport.cs(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/RunSetReport.html(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/css/font-awesome.css(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/css/styles.css(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/js/bootstrap.js(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/js/circlechart.js(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/js/donutchart.js(1 hunks)Ginger/Ginger/Reports/GingerExecutionReport/assets/js/jquery.js(1 hunks)Ginger/GingerCoreNET/Database/DatabaseOperations.cs(5 hunks)Ginger/GingerCoreNET/Reports/GingerExecutionReport/assets/js/bootstrap.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
Reporter.ToLog(eLogLevel.ERROR, message)for logging errors
Files:
Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLDetailedReport.csGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.csGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLSummaryReport.csGinger/GingerCoreNET/Database/DatabaseOperations.cs
🧠 Learnings (5)
📓 Common learnings
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4286
File: Ginger/GingerCoreNET/External/ZAP/ZapProxyService.cs:287-309
Timestamp: 2025-08-29T09:35:46.020Z
Learning: In Ginger ZAP security testing, alert names should preserve their original formatting with spaces and hyphens. When comparing alert names in EvaluateScanResultAPI and similar methods, use StringComparison.OrdinalIgnoreCase for case-insensitive matching but do not normalize by removing spaces or hyphens, as alert names like "False Positive" need to remain as separate words. Confirmed by AmanPrasad43 in PR #4286.
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 3909
File: Ginger/Ginger/RunSetPageLib/NewRunSetPage.xaml.cs:2328-2346
Timestamp: 2024-09-16T16:34:20.667Z
Learning: In `NewRunSetPage.xaml.cs`, the methods `GenerateHTMLReportFromRemote()` and `GenerateHTMLReportFromLocal()` are kept separate because the application first tries to load remote data, and if not found, it falls back to loading local data.
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/{GingerCore,GingerCoreNET,GingerCoreCommon}/Actions/Act*.cs : Name actions following the pattern: `Act{PlatformType}{ActionType}` (e.g., `ActBrowserElement`, `ActConsoleCommand`)
Applied to files:
Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.html
📚 Learning: 2024-09-16T16:34:20.667Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 3909
File: Ginger/Ginger/RunSetPageLib/NewRunSetPage.xaml.cs:2328-2346
Timestamp: 2024-09-16T16:34:20.667Z
Learning: In `NewRunSetPage.xaml.cs`, the methods `GenerateHTMLReportFromRemote()` and `GenerateHTMLReportFromLocal()` are kept separate because the application first tries to load remote data, and if not found, it falls back to loading local data.
Applied to files:
Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.htmlGinger/Ginger/Reports/GingerExecutionReport/RunSetReport.htmlGinger/Ginger/Reports/GingerExecutionReport/ActivityGroupReport.htmlGinger/Ginger/Reports/GingerExecutionReport/BusinessFlowReport.htmlGinger/Ginger/Reports/GingerExecutionReport/GingerExecutionReport.htmlGinger/Ginger/Reports/GingerExecutionReport/GingerRunnerReport.htmlGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLDetailedReport.csGinger/Ginger/Reports/GingerExecutionReport/ActivityReport.htmlGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.csGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLSummaryReport.cs
📚 Learning: 2025-09-30T06:03:09.397Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4314
File: Ginger/GingerCoreNET/RunLib/DynamicExecutionLib/DynamicExecutionManager.cs:960-976
Timestamp: 2025-09-30T06:03:09.397Z
Learning: In GingerPlayDetails configuration mapping (file: Ginger/GingerCoreNET/RunLib/DynamicExecutionLib/DynamicExecutionManager.cs), both EnableAccountReportService and EnableHTMLReportService should be mapped from gingerPlayConfig.GingerPlayReportServiceEnabled as they represent the same underlying service.
Applied to files:
Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLDetailedReport.cs
📚 Learning: 2025-12-18T05:29:42.896Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4385
File: Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs:468-475
Timestamp: 2025-12-18T05:29:42.896Z
Learning: In the Ginger codebase, assume file extensions are lowercase (e.g., .xlsx, .xls). Do not perform case-insensitive extension checks (such as StringComparison.OrdinalIgnoreCase) when using EndsWith() or similar methods to verify file extensions; rely on lowercase comparisons or convert to lowercase first. This ensures consistency and avoids unnecessary case-insensitive comparisons across C# source files.
Applied to files:
Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLDetailedReport.csGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.csGinger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLSummaryReport.csGinger/GingerCoreNET/Database/DatabaseOperations.cs
🧬 Code graph analysis (1)
Ginger/Ginger/Reports/GingerExecutionReport/assets/js/circlechart.js (1)
Ginger/Ginger/Reports/GingerExecutionReport/assets/js/donutchart.js (3)
inputData(2-2)Recharts(10-10)DonutChart(59-76)
🪛 Biome (2.1.2)
Ginger/Ginger/Reports/GingerExecutionReport/assets/css/styles.css
[error] 1-1: Unexpected value or character.
Expected one of:
(parse)
[error] 1-1: Unexpected value or character.
Expected one of:
(parse)
[error] 1-1: Unknown type selector is not allowed.
See MDN web docs for more details.
Consider replacing the unknown type selector with valid one.
(lint/correctness/noUnknownTypeSelector)
[error] 1-1: Generic font family missing.
Consider adding a generic font family as a fallback.
For examples and more information, see the MDN Web Docs
(lint/a11y/useGenericFontNames)
[error] 1-1: Generic font family missing.
Consider adding a generic font family as a fallback.
For examples and more information, see the MDN Web Docs
(lint/a11y/useGenericFontNames)
[error] 1-1: Generic font family missing.
Consider adding a generic font family as a fallback.
For examples and more information, see the MDN Web Docs
(lint/a11y/useGenericFontNames)
[error] 1-1: Generic font family missing.
Consider adding a generic font family as a fallback.
For examples and more information, see the MDN Web Docs
(lint/a11y/useGenericFontNames)
[error] 1-1: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.
background-color is already defined here.
Remove or rename the duplicate property to ensure consistent styling.
(lint/suspicious/noDuplicateProperties)
Ginger/Ginger/Reports/GingerExecutionReport/assets/css/font-awesome.css
[error] 4-4: Unexpected value or character.
Expected one of:
(parse)
[error] 4-4: Unexpected value or character.
Expected one of:
(parse)
[error] 4-4: Unexpected value or character.
Expected one of:
(parse)
[error] 4-4: Unexpected value or character.
Expected one of:
(parse)
[error] 4-4: Unexpected value or character.
Expected one of:
(parse)
[error] 4-4: Generic font family missing.
Consider adding a generic font family as a fallback.
For examples and more information, see the MDN Web Docs
(lint/a11y/useGenericFontNames)
Ginger/Ginger/Reports/GingerExecutionReport/assets/js/jquery.js
[error] 1-1: Comparing to itself is potentially pointless.
(lint/suspicious/noSelfCompare)
[error] 1-1: Do not add then to an object.
(lint/suspicious/noThenProperty)
[error] 1-1: Comparing to itself is potentially pointless.
(lint/suspicious/noSelfCompare)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: Do not reassign a function declaration.
Reassigned here.
Reassigned here.
Reassigned here.
Use a local variable instead.
(lint/suspicious/noFunctionAssign)
[error] 1-1: eval() exposes to security risks and performance issues.
See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().
(lint/security/noGlobalEval)
🔇 Additional comments (6)
Ginger/Ginger/Reports/GingerExecutionReport/assets/js/bootstrap.js (1)
1-6: Bootstrap 3.4.1 upgrade looks good.This is Bootstrap 3.4.1, the latest security release of the 3.x line (February 2019), which includes XSS vulnerability fixes. The jQuery version guard (requires 1.9.1+ but <4) is compatible with the jQuery 2.1.1 included in this PR. This upgrade aligns with the PR's stated goal of "Fixed Bootstrap file by upgrading it."
Ginger/Ginger/Reports/GingerExecutionReport/GingerRunnerReport.html (1)
14-16: JSX scripts require runtime transpilation.The
type="text/jsx"scripts require Babel to transpile JSX at runtime. Ensure the Babel standalone library is included in{js_path}placeholder, otherwise these scripts will not execute.Verify that the JavaScript assets being injected via
{js_path}include Babel standalone for JSX transpilation.Ginger/GingerCoreNET/Reports/GingerExecutionReport/assets/js/bootstrap.js (1)
1-6: Bootstrap upgrade to 3.4.1 looks good.This upgrade to Bootstrap 3.4.1 is the correct approach for maintaining the 3.x line while getting security fixes. The version guard requiring jQuery 1.9.1+ but lower than 4 is appropriate.
Ensure the corresponding jQuery version in the project meets the requirement (≥1.9.1 and <4). You may verify with:
#!/bin/bash # Check jQuery version references in the codebase rg -n "jquery" --type js -g '!bootstrap.js' | head -20 fd -e js "jquery" --exec head -5 {}Ginger/Ginger/Reports/GingerExecutionReport/BusinessFlowReport.html (1)
1-16: LGTM - HTML template structure is well-organized.The template follows a consistent pattern with clear placeholder tokens for dynamic content injection (
{css_path},{js_path},{css_to_place}, etc.) and proper HTML5 structure.Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.cs (2)
40-60: LGTM - Property-based aggregation pattern is clean.The computed properties that delegate to
ReportInfomethods provide a clean abstraction for derived report classes. This keeps the aggregation logic centralized and reusable.
83-105: LGTM - Generic HTML table builder is well-designed.The
CreateHTMLTable<T>method provides a flexible, reusable way to generate HTML tables with custom column extractors. The use ofAggregatewith XElement builders is idiomatic.
| <a href=".\..\..\..\..\GingerExecutionReport.html">{Parent_RunSetReport_Name_Link}</a> | ||
| <small> | ||
| {Parent_RunSetReport_delimiter} | ||
| <a href=".\..\..\..\GingerRunnerReport.html">{Parent_GingerRunner_Name_Link}</a> | ||
| {Parent_GingerRunner_delimiter} | ||
| <a href=".\..\..\BusinessFlowReport.html">{Parent_BusinessFlow_Name_Link}</a> | ||
| {Parent_BusinessFlow_delimiter} | ||
| <!--ActivitiesGroupLink--> | ||
| <a href=".\..\ActivityReport.html">{Parent_Activity_Name_Link}</a> | ||
| {Parent_Activity_delimiter} | ||
| <a href=".\ActionReport.html">{Parent_Action_Name}</a> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use forward slashes for cross-platform path compatibility.
The relative paths use backslashes (e.g., .\..\..\..\..\GingerExecutionReport.html), which are Windows-specific. While browsers typically handle both, using forward slashes is the standard for URLs and ensures cross-platform compatibility.
🔎 Proposed fix
- <a href=".\..\..\..\..\GingerExecutionReport.html">{Parent_RunSetReport_Name_Link}</a>
+ <a href="./../../../../GingerExecutionReport.html">{Parent_RunSetReport_Name_Link}</a>
<small>
{Parent_RunSetReport_delimiter}
- <a href=".\..\..\..\GingerRunnerReport.html">{Parent_GingerRunner_Name_Link}</a>
+ <a href="./../../../GingerRunnerReport.html">{Parent_GingerRunner_Name_Link}</a>
{Parent_GingerRunner_delimiter}
- <a href=".\..\..\BusinessFlowReport.html">{Parent_BusinessFlow_Name_Link}</a>
+ <a href="./../../BusinessFlowReport.html">{Parent_BusinessFlow_Name_Link}</a>
{Parent_BusinessFlow_delimiter}
<!--ActivitiesGroupLink-->
- <a href=".\..\ActivityReport.html">{Parent_Activity_Name_Link}</a>
+ <a href="./../ActivityReport.html">{Parent_Activity_Name_Link}</a>
{Parent_Activity_delimiter}
- <a href=".\ActionReport.html">{Parent_Action_Name}</a>
+ <a href="./ActionReport.html">{Parent_Action_Name}</a>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.html lines 39-49: The
relative links use Windows-style backslashes which are not URL standard; replace
all backslashes in the href attributes (e.g.,
".\..\..\..\..\GingerExecutionReport.html",
".\..\..\..\GingerRunnerReport.html", ".\..\..\BusinessFlowReport.html",
".\..\ActivityReport.html", ".\ActionReport.html") with forward slashes so URLs
read like "./../../../../GingerExecutionReport.html" etc., ensuring all link
paths in this block use forward slashes for cross-platform/browser
compatibility.
| <div id="myModal" class="modal"> | ||
| <span class="close">×</span> | ||
| <img class="modal-content" id="current_image_in_modal"> | ||
| <div id="caption">sdafasd asdfasdf</div> |
There was a problem hiding this comment.
Remove debug placeholder text.
The caption div contains what appears to be debug/placeholder text "sdafasd asdfasdf". This should be removed or replaced with an empty string or a meaningful placeholder.
🔎 Proposed fix
- <div id="caption">sdafasd asdfasdf</div>
+ <div id="caption"></div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div id="caption">sdafasd asdfasdf</div> | |
| <div id="caption"></div> |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.html around line
190, the caption div contains debug placeholder text "sdafasd asdfasdf"; remove
that text and either leave the div empty (e.g., <div id="caption"></div>) or
replace it with a meaningful caption sourced from the appropriate
model/property; if the caption is not available, remove the entire div to avoid
displaying placeholder text.
| document.body.innerHTML = document.body.innerHTML.replace("<!--activitiesgroupquerystring-->", "?Parent_ActivitiesGroup_Folder_Name=" + folderName + "&Parent_ActivitiesGroup_Name=" + Name); | ||
| if (ActivitiesGroupCalledAsRoot == 'true') { | ||
| document.body.innerHTML = document.body.innerHTML.replace("<!--ActivitiesGroupLink-->", "<a href='.\/..\/ActivityGroupReport.html'>" + Name + " </a><i class='ace-icon fa fa-angle-double-right clsc1'></i>"); | ||
| } | ||
| else { | ||
| document.body.innerHTML = document.body.innerHTML.replace("<!--ActivitiesGroupLink-->", "<a href='.\/..\/ActivityGroups\/" + folderName + "\/ActivityGroupReport.html'>" + Name + " </a><i class='ace-icon fa fa-angle-double-right clsc1'></i>"); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider safer DOM manipulation instead of innerHTML replacement.
Using document.body.innerHTML = document.body.innerHTML.replace(...) is inefficient and can break any previously attached event handlers. While this runs on load before user interaction, it's still a fragile pattern. Consider using DOM methods or injecting placeholder elements that can be updated directly.
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActionReport.html around lines
226-232, the code replaces document.body.innerHTML which is fragile and can
break handlers; instead, stop reassigning body.innerHTML and target specific
placeholders: add identifiable placeholders (e.g., span or div with IDs) in the
HTML where the query string and ActivitiesGroup link should go, then in JS build
the query string and create an <a> element (or set href/textContent on an
existing anchor) using DOM APIs
(createElement/setAttribute/textContent/appendChild or element.href and
element.textContent) and update only those nodes; ensure you handle the
ActivitiesGroupCalledAsRoot boolean by setting the appropriate href path and
avoid using innerHTML replacement on the whole document.
| {js_path} | ||
| <script type="text/javascript"> | ||
| { js_to_place } | ||
| </script> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Inconsistent script placement compared to other templates.
The {js_path} placeholder is placed at the bottom of the body (line 162), while in BusinessFlowReport.html it's in the <head> section. This inconsistency could cause issues if scripts depend on load order. Consider standardizing the placement across all templates.
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActivityGroupReport.html around
lines 162-165, the {js_path} placeholder is currently at the bottom of the body
which is inconsistent with BusinessFlowReport.html where it's in the <head>;
update this template to place the {js_path} include in the <head> to match the
other template (or alternatively add defer to the script tag here and update
other templates similarly) and leave the inline { js_to_place } script block in
the body if it relies on DOM being loaded; ensure placement is consistent across
all report templates.
| <div id="myModal" class="modal"> | ||
| <span class="close">×</span> | ||
| <img class="modal-content" id="current_image_in_modal"> | ||
| <div id="caption">sdafasd asdfasdf</div> |
There was a problem hiding this comment.
Remove placeholder/debug text from caption div.
Same issue as in BusinessFlowReport.html - the caption div contains placeholder text "sdafasd asdfasdf".
🔎 Proposed fix
- <div id="caption">sdafasd asdfasdf</div>
+ <div id="caption"></div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div id="caption">sdafasd asdfasdf</div> | |
| <div id="caption"></div> |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActivityGroupReport.html around
line 170, the caption div contains placeholder/debug text "sdafasd asdfasdf";
remove or replace this placeholder with the correct caption content (empty
string, a localized/title value, or the intended dynamic variable) and ensure
any associated data-binding or template variable used elsewhere (as in
BusinessFlowReport.html) is applied here so the caption displays meaningful text
instead of the placeholder.
| </b><br /> | ||
| <b style=""color:#000000;font-family:sans-serif;font-size:12px;font-weight:normal""> | ||
| " + DateTime.Now.ToString() + | ||
| @"</b><br /> |
There was a problem hiding this comment.
Using DateTime.Now instead of actual execution time.
The report displays DateTime.Now as "Execution Run Time", which will show report generation time rather than when the execution actually occurred. This could be misleading if there's a delay between execution and report generation.
Consider using a timestamp from ReportInfo RI if available, such as RI.DateCreated or similar execution timestamp property.
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLSummaryReport.cs
around lines 215 to 218, the template uses DateTime.Now for "Execution Run Time"
which reflects report generation time instead of the actual execution timestamp;
replace DateTime.Now with the execution timestamp from the report metadata
(e.g., RI.DateCreated or the appropriate RI property that stores execution
time), ensure you null-check RI and format the timestamp consistently (ToString
with desired format) before inserting it into the HTML template so the report
shows the real execution time.
| <!-- basic scripts --> | ||
| {js_path} | ||
| <script type="text/javascript"> | ||
| { js_to_place } |
There was a problem hiding this comment.
Inconsistent placeholder syntax.
The placeholder { js_to_place } has extra spaces compared to other placeholders like {js_path} and {css_to_place}. This inconsistency may cause the template processor to fail to replace this placeholder.
🔎 Proposed fix
<script type="text/javascript">
- { js_to_place }
+ {js_to_place}
</script>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { js_to_place } | |
| <script type="text/javascript"> | |
| {js_to_place} | |
| </script> |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/RunSetReport.html around line
158, the placeholder uses inconsistent spacing "{ js_to_place }" which doesn't
match other placeholders like "{js_path}" and "{css_to_place}"; update the
placeholder to "{js_to_place}" (remove the extra spaces) so the template
processor can correctly match and replace it, and scan nearby placeholders to
ensure they use the same no-space "{name}" format.
| <div id="myModal" class="modal"> | ||
| <span class="close">×</span> | ||
| <img class="modal-content" id="current_image_in_modal"> | ||
| <div id="caption">sdafasd asdfasdf</div> |
There was a problem hiding this comment.
Remove placeholder/debug text.
Same debug placeholder text appears in the modal caption.
🔎 Proposed fix
- <div id="caption">sdafasd asdfasdf</div>
+ <div id="caption"></div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div id="caption">sdafasd asdfasdf</div> | |
| <div id="caption"></div> |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/RunSetReport.html around line 164
the div with id="caption" contains placeholder debug text ("sdafasd asdfasdf");
remove that placeholder and replace it with the intended caption content (for
example bind the caption to the run set title or localized string), e.g. use the
actual variable/property that contains the modal caption or a resource lookup
(no hardcoded junk), and ensure the value is properly escaped/encoded for HTML.
| try | ||
| { | ||
| SqlConnectionStringBuilder scSB = new SqlConnectionStringBuilder(); ; | ||
| SqlConnectionStringBuilder scSB = []; |
There was a problem hiding this comment.
Invalid initialization syntax for SqlConnectionStringBuilder.
SqlConnectionStringBuilder is not a collection type and cannot be initialized with the collection expression []. This will cause a compilation error.
🔎 Proposed fix
- SqlConnectionStringBuilder scSB = [];
+ SqlConnectionStringBuilder scSB = new();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| SqlConnectionStringBuilder scSB = []; | |
| SqlConnectionStringBuilder scSB = new(); |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Database/DatabaseOperations.cs around line 136, the code
attempts to initialize SqlConnectionStringBuilder using a collection expression
"[]", which is invalid; replace the incorrect initialization with a proper
constructor call (e.g., new SqlConnectionStringBuilder() or new
SqlConnectionStringBuilder(existingConnectionString)) and then set any
properties on that instance (DataSource, InitialCatalog, UserID, Password,
IntegratedSecurity, etc.) as needed before using it.
| catch (Exception) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to create connection string for DB type: {Database.DBType}, falling back to default", ex); | ||
| Database.ConnectionString = "Data Source=" + Database.TNS + ";User Id={USER};Password={PASS};"; | ||
| } |
There was a problem hiding this comment.
Exception swallowed without logging.
Catching exceptions without logging makes debugging connection string issues extremely difficult. As per coding guidelines, use Reporter.ToLog(eLogLevel.ERROR, message) for logging errors.
🔎 Proposed fix
- catch (Exception)
+ catch (Exception ex)
{
+ Reporter.ToLog(eLogLevel.ERROR, "Failed to create connection string for DB type: " + Database.DBType, ex);
Database.ConnectionString = "Data Source=" + Database.TNS + ";User Id={USER};Password={PASS};";
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| catch (Exception) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to create connection string for DB type: {Database.DBType}, falling back to default", ex); | |
| Database.ConnectionString = "Data Source=" + Database.TNS + ";User Id={USER};Password={PASS};"; | |
| } | |
| catch (Exception ex) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, "Failed to create connection string for DB type: " + Database.DBType, ex); | |
| Database.ConnectionString = "Data Source=" + Database.TNS + ";User Id={USER};Password={PASS};"; | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Database/DatabaseOperations.cs around lines 291 to 294,
the catch block is swallowing all exceptions without logging; change the catch
to capture the exception (e.g., catch (Exception e)) and call
Reporter.ToLog(eLogLevel.ERROR, "<contextual message> " + e.ToString()) before
or after setting Database.ConnectionString so the error and stack trace are
recorded for debugging.
| function show_modal(image_id) { | ||
| var img = document.getElementById(image_id); | ||
| var modalImg = document.getElementById("current_image_in_modal"); | ||
| var captionText = document.getElementById("caption"); | ||
|
|
||
| modal.style.display = "block"; | ||
| modalImg.src = img.src; | ||
| captionText.innerHTML = img.alt; | ||
| } |
There was a problem hiding this comment.
Modal script lacks null safety.
Same issue as in BusinessFlowReport.html - the show_modal function should validate that the element exists.
🔎 Proposed fix
function show_modal(image_id) {
var img = document.getElementById(image_id);
+ if (!img) return;
var modalImg = document.getElementById("current_image_in_modal");
var captionText = document.getElementById("caption");
modal.style.display = "block";
modalImg.src = img.src;
captionText.innerHTML = img.alt;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function show_modal(image_id) { | |
| var img = document.getElementById(image_id); | |
| var modalImg = document.getElementById("current_image_in_modal"); | |
| var captionText = document.getElementById("caption"); | |
| modal.style.display = "block"; | |
| modalImg.src = img.src; | |
| captionText.innerHTML = img.alt; | |
| } | |
| function show_modal(image_id) { | |
| var img = document.getElementById(image_id); | |
| if (!img) return; | |
| var modalImg = document.getElementById("current_image_in_modal"); | |
| var captionText = document.getElementById("caption"); | |
| modal.style.display = "block"; | |
| modalImg.src = img.src; | |
| captionText.innerHTML = img.alt; | |
| } |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActivityGroupReport.html around
lines 177 to 185, the show_modal function does not check that DOM elements (the
image by id, modal, modal image and caption) exist before accessing their
properties; update the function to null-check each element (img, modal,
modalImg, captionText) and return early (or log a warning) if any are missing,
only setting modal.style.display, modalImg.src and captionText.innerHTML when
the corresponding elements are non-null to prevent runtime errors.
| if (folderName == null || Name == null || ActivitiesGroupCalledAsRoot == null || folderName == '' || Name == '') { | ||
|
|
||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Empty if block should have a comment or be restructured.
The empty if block on lines 259-261 is a code smell. Either add a comment explaining why no action is needed, or invert the condition to eliminate the empty block.
🔎 Proposed fix - invert condition
- if (folderName == null || Name == null || ActivitiesGroupCalledAsRoot == null || folderName == '' || Name == '') {
-
- }
- else {
+ if (folderName && Name && ActivitiesGroupCalledAsRoot && folderName !== '' && Name !== '') {
if (ActivitiesGroupCalledAsRoot == 'true') {
document.getElementById("activitiesGroupLink").outerHTML = "<a href='.\/..\/ActivityGroupReport.html'>" + Name + " </a><i class='ace-icon fa fa-angle-double-right clsc1'></i>";
}
else {
document.getElementById("activitiesGroupLink").outerHTML = "<a href='.\/..\/ActivityGroups\/" + folderName + "\/ActivityGroupReport.html'>" + Name + " </a><i class='ace-icon fa fa-angle-double-right clsc1'></i>";
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/ActivityReport.html around lines
259 to 261, there is an empty if block checking if folderName, Name, or
ActivitiesGroupCalledAsRoot are null/empty; invert this condition to remove the
empty block by changing the logic to run the existing code only when all
required values are present (i.e., check for NOT null/empty), or alternatively
add a brief comment inside the empty if explaining why no action is taken;
prefer inverting the condition so the code that follows is executed when
folderName, Name, and ActivitiesGroupCalledAsRoot are valid, eliminating the
empty branch.
| public int ValidationPass { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Pass); } } | ||
| public int validationFail { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Fail); } } | ||
| public int validationOther { get { return ValidationCount - ValidationPass - validationFail; } } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Fix naming inconsistency for validation properties.
validationFail and validationOther use lowercase 'v' while all other properties use PascalCase (e.g., ValidationCount, ValidationPass). This inconsistency can cause confusion.
🔎 Proposed fix
public int ValidationCount { get { return RI.TotalValidationsCount(); } }
public int ValidationPass { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Pass); } }
- public int validationFail { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Fail); } }
- public int validationOther { get { return ValidationCount - ValidationPass - validationFail; } }
+ public int ValidationFail { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Fail); } }
+ public int ValidationOther { get { return ValidationCount - ValidationPass - ValidationFail; } }Note: This change will require updates to HTMLDetailedReport.cs and HTMLSummaryReport.cs which reference these properties.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public int ValidationPass { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Pass); } } | |
| public int validationFail { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Fail); } } | |
| public int validationOther { get { return ValidationCount - ValidationPass - validationFail; } } | |
| public int ValidationPass { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Pass); } } | |
| public int ValidationFail { get { return RI.TotalValidationsCountByStatus(ActReturnValue.eStatus.Fail); } } | |
| public int ValidationOther { get { return ValidationCount - ValidationPass - ValidationFail; } } |
🤖 Prompt for AI Agents
In Ginger/Ginger/Reports/GingerExecutionReport/HTMLReports/HTMLReportBase.cs
around lines 58-60, the properties validationFail and validationOther use
camelCase while the rest are PascalCase; rename them to ValidationFail and
ValidationOther respectively, update all references (notably in
HTMLDetailedReport.cs and HTMLSummaryReport.cs) to the new PascalCase names,
rebuild to ensure no remaining references, and run tests to confirm no behavior
changed.
| * Copyright 2011-2019 Twitter, Inc. | ||
| * Licensed under the MIT license | ||
| */ | ||
| if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(t){"use strict";var e=jQuery.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||3<e[0])throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(),function(n){"use strict";n.fn.emulateTransitionEnd=function(t){var e=!1,i=this;n(this).one("bsTransitionEnd",function(){e=!0});return setTimeout(function(){e||n(i).trigger(n.support.transition.end)},t),this},n(function(){n.support.transition=function o(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var i in e)if(t.style[i]!==undefined)return{end:e[i]};return!1}(),n.support.transition&&(n.event.special.bsTransitionEnd={bindType:n.support.transition.end,delegateType:n.support.transition.end,handle:function(t){if(n(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}})})}(jQuery),function(s){"use strict";var e='[data-dismiss="alert"]',a=function(t){s(t).on("click",e,this.close)};a.VERSION="3.4.1",a.TRANSITION_DURATION=150,a.prototype.close=function(t){var e=s(this),i=e.attr("data-target");i||(i=(i=e.attr("href"))&&i.replace(/.*(?=#[^\s]*$)/,"")),i="#"===i?[]:i;var o=s(document).find(i);function n(){o.detach().trigger("closed.bs.alert").remove()}t&&t.preventDefault(),o.length||(o=e.closest(".alert")),o.trigger(t=s.Event("close.bs.alert")),t.isDefaultPrevented()||(o.removeClass("in"),s.support.transition&&o.hasClass("fade")?o.one("bsTransitionEnd",n).emulateTransitionEnd(a.TRANSITION_DURATION):n())};var t=s.fn.alert;s.fn.alert=function o(i){return this.each(function(){var t=s(this),e=t.data("bs.alert");e||t.data("bs.alert",e=new a(this)),"string"==typeof i&&e[i].call(t)})},s.fn.alert.Constructor=a,s.fn.alert.noConflict=function(){return s.fn.alert=t,this},s(document).on("click.bs.alert.data-api",e,a.prototype.close)}(jQuery),function(s){"use strict";var n=function(t,e){this.$element=s(t),this.options=s.extend({},n.DEFAULTS,e),this.isLoading=!1};function i(o){return this.each(function(){var t=s(this),e=t.data("bs.button"),i="object"==typeof o&&o;e||t.data("bs.button",e=new n(this,i)),"toggle"==o?e.toggle():o&&e.setState(o)})}n.VERSION="3.4.1",n.DEFAULTS={loadingText:"loading..."},n.prototype.setState=function(t){var e="disabled",i=this.$element,o=i.is("input")?"val":"html",n=i.data();t+="Text",null==n.resetText&&i.data("resetText",i[o]()),setTimeout(s.proxy(function(){i[o](null==n[t]?this.options[t]:n[t]),"loadingText"==t?(this.isLoading=!0,i.addClass(e).attr(e,e).prop(e,!0)):this.isLoading&&(this.isLoading=!1,i.removeClass(e).removeAttr(e).prop(e,!1))},this),0)},n.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")?(i.prop("checked")&&(t=!1),e.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==i.prop("type")&&(i.prop("checked")!==this.$element.hasClass("active")&&(t=!1),this.$element.toggleClass("active")),i.prop("checked",this.$element.hasClass("active")),t&&i.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var t=s.fn.button;s.fn.button=i,s.fn.button.Constructor=n,s.fn.button.noConflict=function(){return s.fn.button=t,this},s(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(t){var e=s(t.target).closest(".btn");i.call(e,"toggle"),s(t.target).is('input[type="radio"], input[type="checkbox"]')||(t.preventDefault(),e.is("input,button")?e.trigger("focus"):e.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(t){s(t.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(t.type))})}(jQuery),function(p){"use strict";var c=function(t,e){this.$element=p(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=e,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",p.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",p.proxy(this.pause,this)).on("mouseleave.bs.carousel",p.proxy(this.cycle,this))};function r(n){return this.each(function(){var t=p(this),e=t.data("bs.carousel"),i=p.extend({},c.DEFAULTS,t.data(),"object"==typeof n&&n),o="string"==typeof n?n:i.slide;e||t.data("bs.carousel",e=new c(this,i)),"number"==typeof n?e.to(n):o?e[o]():i.interval&&e.pause().cycle()})}c.VERSION="3.4.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(t){if(!/input|textarea/i.test(t.target.tagName)){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()}},c.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(p.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},c.prototype.getItemForDirection=function(t,e){var i=this.getItemIndex(e);if(("prev"==t&&0===i||"next"==t&&i==this.$items.length-1)&&!this.options.wrap)return e;var o=(i+("prev"==t?-1:1))%this.$items.length;return this.$items.eq(o)},c.prototype.to=function(t){var e=this,i=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(t>this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(i<t?"next":"prev",this.$items.eq(t))},c.prototype.pause=function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&p.support.transition&&(this.$element.trigger(p.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(t,e){var i=this.$element.find(".item.active"),o=e||this.getItemForDirection(t,i),n=this.interval,s="next"==t?"left":"right",a=this;if(o.hasClass("active"))return this.sliding=!1;var r=o[0],l=p.Event("slide.bs.carousel",{relatedTarget:r,direction:s});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,n&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var h=p(this.$indicators.children()[this.getItemIndex(o)]);h&&h.addClass("active")}var d=p.Event("slid.bs.carousel",{relatedTarget:r,direction:s});return p.support.transition&&this.$element.hasClass("slide")?(o.addClass(t),"object"==typeof o&&o.length&&o[0].offsetWidth,i.addClass(s),o.addClass(s),i.one("bsTransitionEnd",function(){o.removeClass([t,s].join(" ")).addClass("active"),i.removeClass(["active",s].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger(d)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(i.removeClass("active"),o.addClass("active"),this.sliding=!1,this.$element.trigger(d)),n&&this.cycle(),this}};var t=p.fn.carousel;p.fn.carousel=r,p.fn.carousel.Constructor=c,p.fn.carousel.noConflict=function(){return p.fn.carousel=t,this};var e=function(t){var e=p(this),i=e.attr("href");i&&(i=i.replace(/.*(?=#[^\s]+$)/,""));var o=e.attr("data-target")||i,n=p(document).find(o);if(n.hasClass("carousel")){var s=p.extend({},n.data(),e.data()),a=e.attr("data-slide-to");a&&(s.interval=!1),r.call(n,s),a&&n.data("bs.carousel").to(a),t.preventDefault()}};p(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),p(window).on("load",function(){p('[data-ride="carousel"]').each(function(){var t=p(this);r.call(t,t.data())})})}(jQuery),function(a){"use strict";var r=function(t,e){this.$element=a(t),this.options=a.extend({},r.DEFAULTS,e),this.$trigger=a('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};function n(t){var e,i=t.attr("data-target")||(e=t.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"");return a(document).find(i)}function l(o){return this.each(function(){var t=a(this),e=t.data("bs.collapse"),i=a.extend({},r.DEFAULTS,t.data(),"object"==typeof o&&o);!e&&i.toggle&&/show|hide/.test(o)&&(i.toggle=!1),e||t.data("bs.collapse",e=new r(this,i)),"string"==typeof o&&e[o]()})}r.VERSION="3.4.1",r.TRANSITION_DURATION=350,r.DEFAULTS={toggle:!0},r.prototype.dimension=function(){return this.$element.hasClass("width")?"width":"height"},r.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var t,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(t=e.data("bs.collapse"))&&t.transitioning)){var i=a.Event("show.bs.collapse");if(this.$element.trigger(i),!i.isDefaultPrevented()){e&&e.length&&(l.call(e,"hide"),t||e.data("bs.collapse",null));var o=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[o](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var n=function(){this.$element.removeClass("collapsing").addClass("collapse in")[o](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return n.call(this);var s=a.camelCase(["scroll",o].join("-"));this.$element.one("bsTransitionEnd",a.proxy(n,this)).emulateTransitionEnd(r.TRANSITION_DURATION)[o](this.$element[0][s])}}}},r.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var t=a.Event("hide.bs.collapse");if(this.$element.trigger(t),!t.isDefaultPrevented()){var e=this.dimension();this.$element[e](this.$element[e]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var i=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};if(!a.support.transition)return i.call(this);this.$element[e](0).one("bsTransitionEnd",a.proxy(i,this)).emulateTransitionEnd(r.TRANSITION_DURATION)}}},r.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},r.prototype.getParent=function(){return a(document).find(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(t,e){var i=a(e);this.addAriaAndCollapsedClass(n(i),i)},this)).end()},r.prototype.addAriaAndCollapsedClass=function(t,e){var i=t.hasClass("in");t.attr("aria-expanded",i),e.toggleClass("collapsed",!i).attr("aria-expanded",i)};var t=a.fn.collapse;a.fn.collapse=l,a.fn.collapse.Constructor=r,a.fn.collapse.noConflict=function(){return a.fn.collapse=t,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(t){var e=a(this);e.attr("data-target")||t.preventDefault();var i=n(e),o=i.data("bs.collapse")?"toggle":e.data();l.call(i,o)})}(jQuery),function(a){"use strict";var r='[data-toggle="dropdown"]',o=function(t){a(t).on("click.bs.dropdown",this.toggle)};function l(t){var e=t.attr("data-target");e||(e=(e=t.attr("href"))&&/#[A-Za-z]/.test(e)&&e.replace(/.*(?=#[^\s]*$)/,""));var i="#"!==e?a(document).find(e):null;return i&&i.length?i:t.parent()}function s(o){o&&3===o.which||(a(".dropdown-backdrop").remove(),a(r).each(function(){var t=a(this),e=l(t),i={relatedTarget:this};e.hasClass("open")&&(o&&"click"==o.type&&/input|textarea/i.test(o.target.tagName)&&a.contains(e[0],o.target)||(e.trigger(o=a.Event("hide.bs.dropdown",i)),o.isDefaultPrevented()||(t.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",i)))))}))}o.VERSION="3.4.1",o.prototype.toggle=function(t){var e=a(this);if(!e.is(".disabled, :disabled")){var i=l(e),o=i.hasClass("open");if(s(),!o){"ontouchstart"in document.documentElement&&!i.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",s);var n={relatedTarget:this};if(i.trigger(t=a.Event("show.bs.dropdown",n)),t.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),i.toggleClass("open").trigger(a.Event("shown.bs.dropdown",n))}return!1}},o.prototype.keydown=function(t){if(/(38|40|27|32)/.test(t.which)&&!/input|textarea/i.test(t.target.tagName)){var e=a(this);if(t.preventDefault(),t.stopPropagation(),!e.is(".disabled, :disabled")){var i=l(e),o=i.hasClass("open");if(!o&&27!=t.which||o&&27==t.which)return 27==t.which&&i.find(r).trigger("focus"),e.trigger("click");var n=i.find(".dropdown-menu li:not(.disabled):visible a");if(n.length){var s=n.index(t.target);38==t.which&&0<s&&s--,40==t.which&&s<n.length-1&&s++,~s||(s=0),n.eq(s).trigger("focus")}}}};var t=a.fn.dropdown;a.fn.dropdown=function e(i){return this.each(function(){var t=a(this),e=t.data("bs.dropdown");e||t.data("bs.dropdown",e=new o(this)),"string"==typeof i&&e[i].call(t)})},a.fn.dropdown.Constructor=o,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=t,this},a(document).on("click.bs.dropdown.data-api",s).on("click.bs.dropdown.data-api",".dropdown form",function(t){t.stopPropagation()}).on("click.bs.dropdown.data-api",r,o.prototype.toggle).on("keydown.bs.dropdown.data-api",r,o.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",o.prototype.keydown)}(jQuery),function(a){"use strict";var s=function(t,e){this.options=e,this.$body=a(document.body),this.$element=a(t),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.fixedContent=".navbar-fixed-top, .navbar-fixed-bottom",this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};function r(o,n){return this.each(function(){var t=a(this),e=t.data("bs.modal"),i=a.extend({},s.DEFAULTS,t.data(),"object"==typeof o&&o);e||t.data("bs.modal",e=new s(this,i)),"string"==typeof o?e[o](n):i.show&&e.show(n)})}s.VERSION="3.4.1",s.TRANSITION_DURATION=300,s.BACKDROP_TRANSITION_DURATION=150,s.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},s.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},s.prototype.show=function(i){var o=this,t=a.Event("show.bs.modal",{relatedTarget:i});this.$element.trigger(t),this.isShown||t.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){o.$element.one("mouseup.dismiss.bs.modal",function(t){a(t.target).is(o.$element)&&(o.ignoreBackdropClick=!0)})}),this.backdrop(function(){var t=a.support.transition&&o.$element.hasClass("fade");o.$element.parent().length||o.$element.appendTo(o.$body),o.$element.show().scrollTop(0),o.adjustDialog(),t&&o.$element[0].offsetWidth,o.$element.addClass("in"),o.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:i});t?o.$dialog.one("bsTransitionEnd",function(){o.$element.trigger("focus").trigger(e)}).emulateTransitionEnd(s.TRANSITION_DURATION):o.$element.trigger("focus").trigger(e)}))},s.prototype.hide=function(t){t&&t.preventDefault(),t=a.Event("hide.bs.modal"),this.$element.trigger(t),this.isShown&&!t.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(s.TRANSITION_DURATION):this.hideModal())},s.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(t){document===t.target||this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},s.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},s.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},s.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},s.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},s.prototype.backdrop=function(t){var e=this,i=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var o=a.support.transition&&i;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+i).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(t){this.ignoreBackdropClick?this.ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide())},this)),o&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!t)return;o?this.$backdrop.one("bsTransitionEnd",t).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):t()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var n=function(){e.removeBackdrop(),t&&t()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",n).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):n()}else t&&t()},s.prototype.handleUpdate=function(){this.adjustDialog()},s.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},s.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"";var n=this.scrollbarWidth;this.bodyIsOverflowing&&(this.$body.css("padding-right",t+n),a(this.fixedContent).each(function(t,e){var i=e.style.paddingRight,o=a(e).css("padding-right");a(e).data("padding-right",i).css("padding-right",parseFloat(o)+n+"px")}))},s.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad),a(this.fixedContent).each(function(t,e){var i=a(e).data("padding-right");a(e).removeData("padding-right"),e.style.paddingRight=i||""})},s.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var t=a.fn.modal;a.fn.modal=r,a.fn.modal.Constructor=s,a.fn.modal.noConflict=function(){return a.fn.modal=t,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(t){var e=a(this),i=e.attr("href"),o=e.attr("data-target")||i&&i.replace(/.*(?=#[^\s]+$)/,""),n=a(document).find(o),s=n.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(i)&&i},n.data(),e.data());e.is("a")&&t.preventDefault(),n.one("show.bs.modal",function(t){t.isDefaultPrevented()||n.one("hidden.bs.modal",function(){e.is(":visible")&&e.trigger("focus")})}),r.call(n,s,this)})}(jQuery),function(g){"use strict";var o=["sanitize","whiteList","sanitizeFn"],a=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],t={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},r=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,l=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function u(t,e){var i=t.nodeName.toLowerCase();if(-1!==g.inArray(i,e))return-1===g.inArray(i,a)||Boolean(t.nodeValue.match(r)||t.nodeValue.match(l));for(var o=g(e).filter(function(t,e){return e instanceof RegExp}),n=0,s=o.length;n<s;n++)if(i.match(o[n]))return!0;return!1}function n(t,e,i){if(0===t.length)return t;if(i&&"function"==typeof i)return i(t);if(!document.implementation||!document.implementation.createHTMLDocument)return t;var o=document.implementation.createHTMLDocument("sanitization");o.body.innerHTML=t;for(var n=g.map(e,function(t,e){return e}),s=g(o.body).find("*"),a=0,r=s.length;a<r;a++){var l=s[a],h=l.nodeName.toLowerCase();if(-1!==g.inArray(h,n))for(var d=g.map(l.attributes,function(t){return t}),p=[].concat(e["*"]||[],e[h]||[]),c=0,f=d.length;c<f;c++)u(d[c],p)||l.removeAttribute(d[c].nodeName);else l.parentNode.removeChild(l)}return o.body.innerHTML}var m=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};m.VERSION="3.4.1",m.TRANSITION_DURATION=150,m.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0},sanitize:!0,sanitizeFn:null,whiteList:t},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){var e=this.$element.data();for(var i in e)e.hasOwnProperty(i)&&-1!==g.inArray(i,o)&&delete e[i];return(t=g.extend({},this.getDefaults(),e,t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t.sanitize&&(t.template=n(t.template,t.whiteList,t.sanitizeFn)),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-d<c.top?"bottom":"right"==s&&l.right+h>c.width?"left":"left"==s&&l.left-h<c.left?"right":s,o.removeClass(p).addClass(s)}var f=this.getCalculatedOffset(s,l,h,d);this.applyPlacement(f,s);var u=function(){var t=i.hoverState;i.$element.trigger("shown.bs."+i.type),i.hoverState=null,"out"==t&&i.leave(i)};g.support.transition&&this.$tip.hasClass("fade")?o.one("bsTransitionEnd",u).emulateTransitionEnd(m.TRANSITION_DURATION):u()}},m.prototype.applyPlacement=function(t,e){var i=this.tip(),o=i[0].offsetWidth,n=i[0].offsetHeight,s=parseInt(i.css("margin-top"),10),a=parseInt(i.css("margin-left"),10);isNaN(s)&&(s=0),isNaN(a)&&(a=0),t.top+=s,t.left+=a,g.offset.setOffset(i[0],g.extend({using:function(t){i.css({top:Math.round(t.top),left:Math.round(t.left)})}},t),0),i.addClass("in");var r=i[0].offsetWidth,l=i[0].offsetHeight;"top"==e&&l!=n&&(t.top=t.top+n-l);var h=this.getViewportAdjustedDelta(e,t,r,l);h.left?t.left+=h.left:t.top+=h.top;var d=/top|bottom/.test(e),p=d?2*h.left-o+r:2*h.top-n+l,c=d?"offsetWidth":"offsetHeight";i.offset(t),this.replaceArrow(p,i[0][c],d)},m.prototype.replaceArrow=function(t,e,i){this.arrow().css(i?"left":"top",50*(1-t/e)+"%").css(i?"top":"left","")},m.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();this.options.html?(this.options.sanitize&&(e=n(e,this.options.whiteList,this.options.sanitizeFn)),t.find(".tooltip-inner").html(e)):t.find(".tooltip-inner").text(e),t.removeClass("fade in top bottom left right")},m.prototype.hide=function(t){var e=this,i=g(this.$tip),o=g.Event("hide.bs."+this.type);function n(){"in"!=e.hoverState&&i.detach(),e.$element&&e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),t&&t()}if(this.$element.trigger(o),!o.isDefaultPrevented())return i.removeClass("in"),g.support.transition&&i.hasClass("fade")?i.one("bsTransitionEnd",n).emulateTransitionEnd(m.TRANSITION_DURATION):n(),this.hoverState=null,this},m.prototype.fixTitle=function(){var t=this.$element;(t.attr("title")||"string"!=typeof t.attr("data-original-title"))&&t.attr("data-original-title",t.attr("title")||"").attr("title","")},m.prototype.hasContent=function(){return this.getTitle()},m.prototype.getPosition=function(t){var e=(t=t||this.$element)[0],i="BODY"==e.tagName,o=e.getBoundingClientRect();null==o.width&&(o=g.extend({},o,{width:o.right-o.left,height:o.bottom-o.top}));var n=window.SVGElement&&e instanceof window.SVGElement,s=i?{top:0,left:0}:n?null:t.offset(),a={scroll:i?document.documentElement.scrollTop||document.body.scrollTop:t.scrollTop()},r=i?{width:g(window).width(),height:g(window).height()}:null;return g.extend({},o,a,r,s)},m.prototype.getCalculatedOffset=function(t,e,i,o){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-i/2}:"top"==t?{top:e.top-o,left:e.left+e.width/2-i/2}:"left"==t?{top:e.top+e.height/2-o/2,left:e.left-i}:{top:e.top+e.height/2-o/2,left:e.left+e.width}},m.prototype.getViewportAdjustedDelta=function(t,e,i,o){var n={top:0,left:0};if(!this.$viewport)return n;var s=this.options.viewport&&this.options.viewport.padding||0,a=this.getPosition(this.$viewport);if(/right|left/.test(t)){var r=e.top-s-a.scroll,l=e.top+s-a.scroll+o;r<a.top?n.top=a.top-r:l>a.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;h<a.left?n.left=a.left-h:d>a.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})},m.prototype.sanitizeHtml=function(t){return n(t,this.options.whiteList,this.options.sanitizeFn)};var e=g.fn.tooltip;g.fn.tooltip=function i(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=e,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.1",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();if(this.options.html){var o=typeof i;this.options.sanitize&&(e=this.sanitizeHtml(e),"string"===o&&(i=this.sanitizeHtml(i))),t.find(".popover-title").html(e),t.find(".popover-content").children().detach().end()["string"===o?"html":"append"](i)}else t.find(".popover-title").text(e),t.find(".popover-content").children().detach().end().text(i);t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.1",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e<n[0])return this.activeTarget=null,this.clear();for(t=n.length;t--;)a!=s[t]&&e>=n[t]&&(n[t+1]===undefined||e<n[t+1])&&this.activate(s[t])},n.prototype.activate=function(t){this.activeTarget=t,this.clear();var e=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',i=s(e).parents("li").addClass("active");i.parent(".dropdown-menu").length&&(i=i.closest("li.dropdown").addClass("active")),i.trigger("activate.bs.scrollspy")},n.prototype.clear=function(){s(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=s.fn.scrollspy;s.fn.scrollspy=e,s.fn.scrollspy.Constructor=n,s.fn.scrollspy.noConflict=function(){return s.fn.scrollspy=t,this},s(window).on("load.bs.scrollspy.data-api",function(){s('[data-spy="scroll"]').each(function(){var t=s(this);e.call(t,t.data())})})}(jQuery),function(r){"use strict";var a=function(t){this.element=r(t)};function e(i){return this.each(function(){var t=r(this),e=t.data("bs.tab");e||t.data("bs.tab",e=new a(this)),"string"==typeof i&&e[i]()})}a.VERSION="3.4.1",a.TRANSITION_DURATION=150,a.prototype.show=function(){var t=this.element,e=t.closest("ul:not(.dropdown-menu)"),i=t.data("target");if(i||(i=(i=t.attr("href"))&&i.replace(/.*(?=#[^\s]*$)/,"")),!t.parent("li").hasClass("active")){var o=e.find(".active:last a"),n=r.Event("hide.bs.tab",{relatedTarget:t[0]}),s=r.Event("show.bs.tab",{relatedTarget:o[0]});if(o.trigger(n),t.trigger(s),!s.isDefaultPrevented()&&!n.isDefaultPrevented()){var a=r(document).find(i);this.activate(t.closest("li"),e),this.activate(a,a.parent(),function(){o.trigger({type:"hidden.bs.tab",relatedTarget:t[0]}),t.trigger({type:"shown.bs.tab",relatedTarget:o[0]})})}}},a.prototype.activate=function(t,e,i){var o=e.find("> .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.1",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n<i&&"top";if("bottom"==this.affixed)return null!=i?!(n+this.unpin<=s.top)&&"bottom":!(n+a<=t-o)&&"bottom";var r=null==this.affixed,l=r?n:s.top;return null!=i&&n<=i?"top":null!=o&&t-o<=l+(r?a:e)&&"bottom"},h.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(h.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},h.prototype.checkPositionWithEventLoop=function(){setTimeout(l.proxy(this.checkPosition,this),1)},h.prototype.checkPosition=function(){if(this.$element.is(":visible")){var t=this.$element.height(),e=this.options.offset,i=e.top,o=e.bottom,n=Math.max(l(document).height(),l(document.body).height());"object"!=typeof e&&(o=i=e),"function"==typeof i&&(i=e.top(this.$element)),"function"==typeof o&&(o=e.bottom(this.$element));var s=this.getState(n,t,i,o);if(this.affixed!=s){null!=this.unpin&&this.$element.css("top","");var a="affix"+(s?"-"+s:""),r=l.Event(a+".bs.affix");if(this.$element.trigger(r),r.isDefaultPrevented())return;this.affixed=s,this.unpin="bottom"==s?this.getPinnedOffset():null,this.$element.removeClass(h.RESET).addClass(a).trigger(a.replace("affix","affixed")+".bs.affix")}"bottom"==s&&this.$element.offset({top:n-t-o})}};var t=l.fn.affix;l.fn.affix=i,l.fn.affix.Constructor=h,l.fn.affix.noConflict=function(){return l.fn.affix=t,this},l(window).on("load",function(){l('[data-spy="affix"]').each(function(){var t=l(this),e=t.data();e.offset=e.offset||{},null!=e.offsetBottom&&(e.offset.bottom=e.offsetBottom),null!=e.offsetTop&&(e.offset.top=e.offsetTop),i.call(t,e)})})}(jQuery); No newline at end of file |
Check failure
Code scanning / CodeQL
DOM text reinterpreted as HTML High
Copilot Autofix
AI 7 months ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| * Bootstrap v3.4.1 (https://getbootstrap.com/) | ||
| * Copyright 2011-2019 Twitter, Inc. | ||
| * Licensed under the MIT license | ||
| */ |
Check failure
Code scanning / CodeQL
DOM text reinterpreted as HTML High
Copilot Autofix
AI 7 months ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Ginger/GingerCoreCommon/GingerCoreCommon.csproj (1)
10-12: Add Windows 10.0.17763.0 compatibility specification.Per coding guidelines, .csproj files targeting .NET 8.0 should also specify Windows 10.0.17763.0 compatibility. Consider adding the appropriate property (e.g.,
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>for Windows-specific projects or<TargetPlatformMinVersion>if this is a Windows-only library).Based on coding guidelines stating: "Target framework should be .NET 8.0 with Windows 10.0.17763.0 compatibility"
🔎 Suggested fix
<PropertyGroup> <TargetFramework>net8.0</TargetFramework> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <RootNamespace>Amdocs.Ginger.Common</RootNamespace>
📜 Review details
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (11)
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xamlGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerAutoPilot/APIModelLib/SwaggerApi/OpenApiBase.csGinger/GingerCore/GeneralLib/InputBoxWindow.xamlGinger/GingerCoreCommon/GingerCoreCommon.csprojGinger/GingerCoreCommon/InterfacesLib/IExcelOperations.csGinger/GingerCoreNET/ActionsLib/ActExcel.csGinger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.csGinger/GingerCoreTest/GingerCoreTest.csprojGinger/GingerTest/GingerTest.csproj
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
Reporter.ToLog(eLogLevel.ERROR, message)for logging errors
Files:
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.csGinger/GingerAutoPilot/APIModelLib/SwaggerApi/OpenApiBase.csGinger/GingerCoreCommon/InterfacesLib/IExcelOperations.csGinger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
**/*.csproj
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Target framework should be .NET 8.0 with Windows 10.0.17763.0 compatibility
Files:
Ginger/GingerCoreTest/GingerCoreTest.csprojGinger/GingerCoreCommon/GingerCoreCommon.csprojGinger/GingerTest/GingerTest.csproj
**/{Ginger,GingerCore}/**/{*Page,*Window}.xaml.cs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name pages and windows following the pattern:
{Feature}Pageor{Feature}Window
Files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
🧠 Learnings (27)
📓 Common learnings
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4286
File: Ginger/GingerCoreNET/External/ZAP/ZapProxyService.cs:287-309
Timestamp: 2025-08-29T09:35:46.020Z
Learning: In Ginger ZAP security testing, alert names should preserve their original formatting with spaces and hyphens. When comparing alert names in EvaluateScanResultAPI and similar methods, use StringComparison.OrdinalIgnoreCase for case-insensitive matching but do not normalize by removing spaces or hyphens, as alert names like "False Positive" need to remain as separate words. Confirmed by AmanPrasad43 in PR #4286.
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/*Test.cs : Test files should end with `Test.cs` and use MSTest framework with `[TestClass]` and `[TestMethod]` attributes
Applied to files:
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.cs
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/{GingerCore,GingerCoreNET,GingerCoreCommon}/Actions/Act*.cs : Name actions following the pattern: `Act{PlatformType}{ActionType}` (e.g., `ActBrowserElement`, `ActConsoleCommand`)
Applied to files:
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2024-07-26T22:04:12.930Z
Learnt from: Maheshkale447
Repo: Ginger-Automation/Ginger PR: 3637
File: Ginger/GingerCoreNET/Application Models/Execution/POM/POMExecutionUtils.cs:226-234
Timestamp: 2024-07-26T22:04:12.930Z
Learning: The `LastUpdatedTime` in the `POMExecutionUtils.cs` file of the Ginger project is always in the correct DateTime format, as confirmed by the user. This information is crucial for avoiding unnecessary error handling suggestions in future reviews.
Applied to files:
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.cs
📚 Learning: 2025-12-18T05:29:42.896Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4385
File: Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs:468-475
Timestamp: 2025-12-18T05:29:42.896Z
Learning: In the Ginger codebase, assume file extensions are lowercase (e.g., .xlsx, .xls). Do not perform case-insensitive extension checks (such as StringComparison.OrdinalIgnoreCase) when using EndsWith() or similar methods to verify file extensions; rely on lowercase comparisons or convert to lowercase first. This ensures consistency and avoids unnecessary case-insensitive comparisons across C# source files.
Applied to files:
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.csGinger/GingerAutoPilot/APIModelLib/SwaggerApi/OpenApiBase.csGinger/GingerCoreCommon/InterfacesLib/IExcelOperations.csGinger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-08-25T08:48:11.915Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4281
File: Ginger/GingerCore/GingerCore.csproj:538-538
Timestamp: 2025-08-25T08:48:11.915Z
Learning: In the Ginger codebase, the OWASPZAPDotNetAPI package reference in GingerCore.csproj is required for architectural reasons, even when direct usage is not immediately visible in the codebase. This was confirmed by AmanPrasad43 during the security testing feature implementation.
Applied to files:
Ginger/GingerCoreTest/GingerCoreTest.csprojGinger/GingerCoreCommon/GingerCoreCommon.csprojGinger/GingerTest/GingerTest.csprojGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-03-20T11:10:33.780Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4137
File: Ginger/Ginger/SourceControl/SourceControlProjectsPage.xaml.cs:21-21
Timestamp: 2025-03-20T11:10:33.780Z
Learning: The `Amdocs.Ginger.Common.SourceControlLib` namespace is required in files that reference the `GingerSolution` class for source control operations in the Ginger automation framework.
Applied to files:
Ginger/GingerCoreTest/GingerCoreTest.csprojGinger/GingerCoreCommon/GingerCoreCommon.csprojGinger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/GingerTest/GingerTest.csprojGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-01-16T04:09:15.662Z
Learnt from: IamRanjeetSingh
Repo: Ginger-Automation/Ginger PR: 4058
File: Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/ActionHandlers/ActUIElementHandler.cs:648-649
Timestamp: 2025-01-16T04:09:15.662Z
Learning: The Ginger project primarily targets .NET 8.0, which supports C# 12 features including collection expressions with spread operator.
Applied to files:
Ginger/GingerCoreTest/GingerCoreTest.csproj
📚 Learning: 2025-08-26T07:40:08.345Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4285
File: Ginger/Ginger/Actions/ActionEditPages/ActSecurityTestingEditPage.xaml:23-23
Timestamp: 2025-08-26T07:40:08.345Z
Learning: In the Ginger codebase ActSecurityTestingEditPage.xaml file, the large bottom margin of 530 on the "Acceptable Alerts" label (Margin="10,10,0,530") is intentional design choice, not an error.
Applied to files:
Ginger/GingerCore/GeneralLib/InputBoxWindow.xamlGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml
📚 Learning: 2025-02-24T18:12:09.279Z
Learnt from: IamRanjeetSingh
Repo: Ginger-Automation/Ginger PR: 4128
File: Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/ActionHandlers/ActUIElementHandler.cs:322-325
Timestamp: 2025-02-24T18:12:09.279Z
Learning: For text length calculation in Ginger Automation's Web driver, only the value attribute should be used without any fallbacks to text content or inner text.
Applied to files:
Ginger/GingerCore/GeneralLib/InputBoxWindow.xaml
📚 Learning: 2025-04-08T12:31:45.924Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4169
File: Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/ActionHandlers/ActUIElementHandler.cs:321-336
Timestamp: 2025-04-08T12:31:45.924Z
Learning: The GetTextLengthAsync method in Ginger Automation's Web driver should attempt to retrieve text from multiple sources (TextContent, InnerText, InputValue) to calculate text length, providing enhanced capabilities to read text length from different types of elements.
Applied to files:
Ginger/GingerCore/GeneralLib/InputBoxWindow.xaml
📚 Learning: 2025-12-18T05:19:56.687Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4385
File: Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml:0-0
Timestamp: 2025-12-18T05:19:56.687Z
Learning: In XAML DataTriggers, enum properties can be compared to string values because WPF automatically converts enums to strings during evaluation. This pattern is valid for XAML files like ActExcelEditPage.xaml where eExcelActionType is compared to string literals such as "ReadCellByIndex" or "GetSheetDetails". Apply this guideline broadly to XAML files that bind to enum properties; verify that such comparisons behave as expected in the specific UI components and avoid relying on string literals if the enum values change.
Applied to files:
Ginger/GingerCore/GeneralLib/InputBoxWindow.xamlGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/Ginger.PluginPackage.json : Plugin packages should be defined by `Ginger.PluginPackage.json` manifest
Applied to files:
Ginger/GingerCoreCommon/GingerCoreCommon.csproj
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/*.csproj : Target framework should be .NET 8.0 with Windows 10.0.17763.0 compatibility
Applied to files:
Ginger/GingerCoreCommon/GingerCoreCommon.csproj
📚 Learning: 2025-04-25T13:29:45.059Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4188
File: Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs:22-23
Timestamp: 2025-04-25T13:29:45.059Z
Learning: The `using Amdocs.Ginger.Repository;` statement in Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs is necessary as it provides access to the `ObservableList<>` class which is used throughout the file for collections of RunSetConfig, AnalyzerItemBase, and ApplicationPOMModel objects.
Applied to files:
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-04-25T13:29:45.059Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4188
File: Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs:22-23
Timestamp: 2025-04-25T13:29:45.059Z
Learning: The `using Amdocs.Ginger.Repository;` statement in Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs is necessary as it imports the ObservableList<> class which is used throughout the file.
Applied to files:
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-06-16T10:37:13.073Z
Learnt from: prashelke
Repo: Ginger-Automation/Ginger PR: 4232
File: Ginger/GingerCoreNET/ActionsLib/UI/VisualTesting/VRTAnalyzer.cs:19-22
Timestamp: 2025-06-16T10:37:13.073Z
Learning: In Ginger codebase, both `using amdocs.ginger.GingerCoreNET;` and `using Amdocs.Ginger.CoreNET;` are valid and serve different purposes. The first (lowercase) contains the WorkSpace class and related workspace functionality, while the second (proper case) contains drivers like GenericAppiumDriver and other core functionality. Both may be required in files that use types from both namespaces.
Applied to files:
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.csGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2024-07-26T22:04:12.930Z
Learnt from: prashelke
Repo: Ginger-Automation/Ginger PR: 3429
File: Ginger/Ginger/AutomatePageLib/AddActionMenu/WindowExplorer/Common/WindowExplorerPage.xaml.cs:1123-1126
Timestamp: 2024-07-26T22:04:12.930Z
Learning: The user has removed an unnecessary empty line at 1123 in `WindowExplorerPage.xaml.cs` for better code compactness.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xamlGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-04-30T10:48:07.912Z
Learnt from: rathimayur
Repo: Ginger-Automation/Ginger PR: 4195
File: Ginger/Ginger/Actions/ActionEditPage.xaml.cs:2523-2536
Timestamp: 2025-04-30T10:48:07.912Z
Learning: In the ActionEditPage, the browse button is only visible/clickable when there are items in the grid, preventing null reference exceptions when accessing CurrentItem.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml
📚 Learning: 2025-07-09T13:46:53.701Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4245
File: Ginger/Ginger/Actions/ActionEditPages/ValidationDBPage.xaml.cs:142-143
Timestamp: 2025-07-09T13:46:53.701Z
Learning: In ValidationDBPage.xaml.cs, the new constructor overload that accepts SelectedContentArgs and ActDBValidation intentionally removes the SelectionChanged event handlers for AppNameComboBox and DBNameComboBox without restoring them. This is expected behavior since this constructor sets up the combo boxes with fixed values and makes them non-editable, so the normal selection changed behavior is not needed.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xamlGinger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-07-16T14:42:32.229Z
Learnt from: prashelke
Repo: Ginger-Automation/Ginger PR: 4254
File: Ginger/Ginger/ApplicationModelsLib/POMModels/POMWizardLib/LearnWizard/POMLearnConfigWizardPage.xaml.cs:70-73
Timestamp: 2025-07-16T14:42:32.229Z
Learning: In Ginger/Ginger/ApplicationModelsLib/POMModels/POMWizardLib/LearnWizard/POMLearnConfigWizardPage.xaml.cs, prashelke prefers to keep the current `!WorkSpace.Instance.BetaFeatures.ShowPOMForAI` logic for binding and showing AI POM controls, rather than changing it to positive logic.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-12-18T05:20:03.536Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4385
File: Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml:0-0
Timestamp: 2025-12-18T05:20:03.536Z
Learning: In the Ginger codebase, WPF DataTriggers can use string values (e.g., "ReadCellByIndex", "GetSheetDetails") to compare against enum properties (e.g., eExcelActionType) because WPF automatically handles enum-to-string conversion during DataTrigger evaluation. This pattern is confirmed to work correctly and is acceptable in XAML files.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.csGinger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-08-28T09:29:59.151Z
Learnt from: AmanPrasad43
Repo: Ginger-Automation/Ginger PR: 4286
File: Ginger/Ginger/Drivers/DriversConfigsEditPages/WebServicesDriverEditPage.xaml.cs:149-152
Timestamp: 2025-08-28T09:29:59.151Z
Learning: In Ginger's WebServicesDriverEditPage.xaml.cs, the FillComboItemsFromEnumType method should be used instead of FillComboFromEnumType when sorting by enum order is required. FillComboItemsFromEnumType preserves EnumValueDescription display through WPF's automatic ToString() calling on enum Content, and provides built-in sorting functionality via SortDescriptions.Add("text", Ascending). This was confirmed by AmanPrasad43 in PR #4286 for the ZAP vulnerability type dropdown.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-07-09T13:44:51.210Z
Learnt from: GokulBothe99
Repo: Ginger-Automation/Ginger PR: 4245
File: Ginger/Ginger/UserControlsLib/UCDataMapping.xaml.cs:658-667
Timestamp: 2025-07-09T13:44:51.210Z
Learning: In UCDataMapping.xaml.cs SetDatabaseValues() method, the line xDBValueExpression.Visibility = Visibility.Visible is intentionally set unconditionally and works correctly with the visibility logic in SetValueControlsView(). This is the expected behavior per user confirmation.
Applied to files:
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/{GingerCore,GingerCoreNET,GingerCoreCommon}/Actions/Act*.cs : Actions should inherit from `Act` class and implement platform-specific logic
Applied to files:
Ginger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-11-28T05:32:43.529Z
Learnt from: CR
Repo: Ginger-Automation/Ginger PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T05:32:43.529Z
Learning: Applies to **/{GingerCore,GingerCoreNET,GingerCoreCommon}/Actions/Act*.cs : On action failure, set `act.Error` and `act.Status = eRunStatus.Failed`
Applied to files:
Ginger/GingerCoreNET/ActionsLib/ActExcel.cs
📚 Learning: 2025-06-16T09:52:40.873Z
Learnt from: prashelke
Repo: Ginger-Automation/Ginger PR: 4232
File: Ginger/GingerCoreNET/ActionsLib/UI/VisualTesting/VRTAnalyzer.cs:19-22
Timestamp: 2025-06-16T09:52:40.873Z
Learning: The `GenericAppiumDriver` class is located in the `Amdocs.Ginger.CoreNET` namespace, not in a namespace that matches its file path structure. File paths don't always correspond to namespace declarations in this codebase.
Applied to files:
Ginger/GingerCoreNET/ActionsLib/ActExcel.cs
🧬 Code graph analysis (5)
Ginger/GingerAutoPilot/APIModelLib/SwaggerApi/OpenApiBase.cs (2)
Ginger/GingerCoreCommon/Repository/ApplicationModelLib/ApplicationModelBase.cs (1)
ToString(53-56)Ginger/GingerCoreCommon/Repository/RepositoryFolder.cs (1)
ToString(87-90)
Ginger/GingerCoreCommon/InterfacesLib/IExcelOperations.cs (1)
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs (1)
WriteCellData(460-522)
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs (1)
Ginger/GingerCoreCommon/InterfacesLib/IExcelOperations.cs (1)
WriteCellData(86-86)
Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs (2)
Ginger/GingerCoreNET/ActionsLib/ActExcel.cs (5)
ActExcel(37-653)ReadData(370-474)ReadCellData(495-537)WriteData(539-603)GetSheetDetails(292-363)Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs (1)
WriteData(282-296)
Ginger/GingerCoreNET/ActionsLib/ActExcel.cs (3)
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs (8)
WriteData(282-296)DataTable(51-73)DataTable(92-108)DataTable(110-127)DataTable(152-155)DataTable(198-280)WriteCellData(460-522)List(333-351)Ginger/GingerCoreCommon/InterfacesLib/IExcelOperations.cs (6)
WriteData(49-49)DataTable(35-35)DataTable(37-37)DataTable(59-59)WriteCellData(86-86)List(75-75)Ginger/GingerCoreCommon/Actions/Act.cs (3)
AddOrUpdateInputParamValue(828-851)AddOrUpdateReturnParamActual(1308-1335)AddOrUpdateReturnParamActualWithPath(1359-1387)
🔇 Additional comments (16)
Ginger/GingerCore/GeneralLib/InputBoxWindow.xaml (1)
27-27: Make the MaxLength configurable or remove the hardcoded 30-character limit.The added
MaxLength="30"restricts input across all usages of InputBoxWindow, which is a general-purpose modal dialog used for naming solutions, folders, parameters, APIs, tables, and other entities throughout the application—not just for JavaScript alerts. The hardcoded limit is inappropriate for this diverse set of use cases and could prevent legitimate longer names.Ginger/GingerTest/GingerTest.csproj (1)
27-27: Remove unused Microsoft.Office.Interop.Excel package from GingerTest.csproj.This package is not used in the codebase; Excel operations use OfficeOpenXml (EPPlus) instead. Remove the package reference entirely rather than maintaining an unused dependency.
Likely an incorrect or invalid review comment.
Ginger/GingerAutoPilot/APIModelLib/SwaggerApi/OpenApiBase.cs (1)
102-122: PR title doesn't match the code changes.The PR title indicates "JavaScript alerts fix," but this change truncates OpenAPI operation summaries to 50 characters—unrelated to JavaScript alerts. Please verify this is the correct PR or update the title and description to reflect the actual changes.
Ginger/GingerCoreNETUnitTest/LinuxTransformationTests/ActExcelTests.cs (1)
343-344: LGTM!This is a formatting-only change (trailing newline). The existing test coverage for Excel operations is comprehensive.
Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs (1)
21-24: LGTM!The new
usingdirectives forNPOI.HSSF.UserModelandNPOI.XSSF.UserModelare correctly added to support the multi-format Excel handling in the newWriteCellDatamethod.Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml (2)
47-50: LGTM!The new radio button panel for data selection method (By Parameters / By Cell Address) is well-structured and follows the existing UI conventions. The visibility binding to
Collapsedby default is appropriate since it will be controlled programmatically.
69-72: LGTM!The new
xAddressInputPanelwith cell address input properly supports the new ByCellAddress data selection mode. The tooltip provides helpful guidance for users.Ginger/GingerCoreNET/ActionsLib/ActExcel.cs (7)
79-128: LGTM!The enum definitions with explicit integral values ensure serialization stability. The new properties follow the established input parameter patterns used elsewhere in the class.
174-186: LGTM!The conditional logic correctly routes to the appropriate filter source based on the data selection method, maintaining backward compatibility for the ByParameters mode.
243-272: LGTM!The execution flow correctly validates mandatory fields and routes to the appropriate action handler. The warning for unexpected action types is appropriate.
277-287: LGTM!The column name conversion algorithm correctly handles both single-letter (A-Z) and multi-letter (AA, AB, etc.) Excel column names using base-26 arithmetic.
292-363: LGTM!The implementation correctly calculates sheet dimensions with defensive parsing and appropriate error handling using
Reporter.ToLog(eLogLevel.ERROR, ...). The null DataTable case is properly handled.
539-603: LGTM!The WriteData method correctly branches between ByCellAddress (using the new
WriteCellDatamethod) and ByParameters modes. Error handling usesReporter.ToLog(eLogLevel.ERROR, ...)per coding guidelines.
370-474: LGTM!The ReadData method correctly implements both ByCellAddress and ByParameters modes with appropriate cell address tracking when
PullCellAddressis enabled. Error handling usesReporter.ToLog(eLogLevel.ERROR, ...)per coding guidelines.Ginger/GingerCoreCommon/GingerCoreCommon.csproj (2)
21-21: Copyright year update looks good.The copyright year range has been appropriately updated to include 2026.
46-48: System.Net.Http 4.3.4 and System.Text.RegularExpressions 4.3.1 are actively used and secure.System.Net.Http 4.3.4 is a patched version addressing CVE-2018-8292, and System.Text.RegularExpressions 4.3.1 has no detected risks and is safe to use. Both packages are actively used within GingerCoreCommon: System.Net.Http in HttpUtilities.cs and System.Text.RegularExpressions in multiple files including pattern matching and serialization logic. These explicit references are necessary for .NET 8.0 compatibility with this project's functionality.
Likely an incorrect or invalid review comment.
| using Amdocs.Ginger.Common.InterfacesLib; | ||
| using Amdocs.Ginger.CoreNET.ActionsLib; | ||
| using GingerCore.Actions; | ||
| using Org.BouncyCastle.Ocsp; |
There was a problem hiding this comment.
Remove unused using directive.
The using Org.BouncyCastle.Ocsp; directive appears to be unused in this file. It should be removed to avoid confusion and keep the imports clean.
🔎 Proposed fix
using GingerCore.Actions;
-using Org.BouncyCastle.Ocsp;
using System;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| using Org.BouncyCastle.Ocsp; | |
| using GingerCore.Actions; | |
| using System; |
🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs at line 24,
the using directive "using Org.BouncyCastle.Ocsp;" is unused; remove that using
line from the file to clean up imports and avoid unnecessary references.
| //GingerCore.GeneralLib.BindingHandler.ObjFieldBinding(rdByAddress, RadioButton.IsCheckedProperty, mAct, nameof(ActExcel.eDataSelectionMethod)); | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Remove commented-out code in Bind() method.
Line 61 contains a commented-out binding statement. This should be removed if no longer needed.
🔎 Proposed fix
public void Bind()
{
- //GingerCore.GeneralLib.BindingHandler.ObjFieldBinding(rdByAddress, RadioButton.IsCheckedProperty, mAct, nameof(ActExcel.eDataSelectionMethod));
-
ExcelActionComboBox.BindControl(mAct, nameof(ActExcel.ExcelActionType));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //GingerCore.GeneralLib.BindingHandler.ObjFieldBinding(rdByAddress, RadioButton.IsCheckedProperty, mAct, nameof(ActExcel.eDataSelectionMethod)); | |
| public void Bind() | |
| { | |
| ExcelActionComboBox.BindControl(mAct, nameof(ActExcel.ExcelActionType)); |
🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs around lines
61-62 there is a commented-out binding line
(//GingerCore.GeneralLib.BindingHandler.ObjFieldBinding(rdByAddress,
RadioButton.IsCheckedProperty, mAct, nameof(ActExcel.eDataSelectionMethod));)
inside the Bind() method; remove this commented-out code line to clean up dead
code and save the file, leaving no commented binding statements in that method.
| bool showRadios = mAct.ExcelActionType is ActExcel.eExcelActionType.ReadData or ActExcel.eExcelActionType.ReadCellData or ActExcel.eExcelActionType.WriteData; xRadioDataSelectionPanel.Visibility = showRadios ? Visibility.Visible : Visibility.Collapsed; | ||
|
|
||
| // 2. Determine if "By Address" is active | ||
| // Safety check: rdByAddress might be null during init, so we check visibility too | ||
| bool isByAddress = (mAct.GetInputParamValue(nameof(mAct.DataSelectionMethod)) == "ByCellAddress" ? true : false); | ||
| rdByAddress.IsChecked = isByAddress; | ||
| rdByParams.IsChecked = !isByAddress; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use enum comparison instead of string comparison for DataSelectionMethod.
Line 95 uses a string comparison == "ByCellAddress" which is fragile and error-prone. Use the enum type directly for type-safe comparison. Also, the ternary ? true : false is redundant.
🔎 Proposed fix
- bool showRadios = mAct.ExcelActionType is ActExcel.eExcelActionType.ReadData or ActExcel.eExcelActionType.ReadCellData or ActExcel.eExcelActionType.WriteData; xRadioDataSelectionPanel.Visibility = showRadios ? Visibility.Visible : Visibility.Collapsed;
+ bool showRadios = mAct.ExcelActionType is ActExcel.eExcelActionType.ReadData
+ or ActExcel.eExcelActionType.ReadCellData
+ or ActExcel.eExcelActionType.WriteData;
+ xRadioDataSelectionPanel.Visibility = showRadios ? Visibility.Visible : Visibility.Collapsed;
// 2. Determine if "By Address" is active
// Safety check: rdByAddress might be null during init, so we check visibility too
- bool isByAddress = (mAct.GetInputParamValue(nameof(mAct.DataSelectionMethod)) == "ByCellAddress" ? true : false);
+ bool isByAddress = mAct.DataSelectionMethod == ActExcel.eDataSelectionMethod.ByCellAddress;
rdByAddress.IsChecked = isByAddress;
rdByParams.IsChecked = !isByAddress;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs around lines
91 to 97, replace the fragile string comparison of
mAct.GetInputParamValue(nameof(mAct.DataSelectionMethod)) == "ByCellAddress" and
the redundant ternary with a type-safe enum check: retrieve the
DataSelectionMethod input as the ActExcel.eDataSelectionMethod (or the correct
enum type) using the appropriate getter/parse, compare it directly to the enum
value (e.g., ActExcel.eDataSelectionMethod.ByCellAddress) to set isByAddress,
and remove the ? true : false; keep the existing visibility/null safety for
rdByAddress and rdByParams when assigning IsChecked.
| if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData ) | ||
| { | ||
| FillSheetCombo(); | ||
| ColMappingRulesSection.Visibility = Visibility.Visible; | ||
| } | ||
| else | ||
| { | ||
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | ||
| } | ||
|
|
||
| // 6. Bulk Write Field (ColMappingRules) | ||
| // Show ONLY for WriteData in PARAMETER mode. | ||
| if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && isByAddress) | ||
| { | ||
| ColMappingRulesSection.Visibility = Visibility.Visible; | ||
| SetDataUsedSection.Visibility = Visibility.Collapsed; | ||
|
|
||
| } | ||
| else | ||
| { | ||
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | ||
| SetDataUsedSection.Visibility = Visibility.Collapsed; | ||
| } |
There was a problem hiding this comment.
Visibility logic has conflicting/redundant assignments for ColMappingRulesSection.
The visibility logic has overlapping conditions:
- Lines 132-139: Sets
ColMappingRulesSection.Visibility = Visiblefor WriteData, elseCollapsed - Lines 143-153: Immediately after, sets
ColMappingRulesSection.Visibilityagain based onWriteData && isByAddress
The first block's assignment at lines 134-138 is immediately overwritten by the second block. This appears to be a logic error or dead code.
🔎 Proposed fix
- // 5. Write Value Field (SetDataUsed)
- // Show for WriteData (Always)
- // Show for ReadData
- // HIDE for ReadCellData
- if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData )
- {
- ColMappingRulesSection.Visibility = Visibility.Visible;
- }
- else
- {
- ColMappingRulesSection.Visibility = Visibility.Collapsed;
- }
-
- // 6. Bulk Write Field (ColMappingRules)
- // Show ONLY for WriteData in PARAMETER mode.
+ // 5. Write Value and Bulk Write Fields
+ // ColMappingRulesSection: Show for WriteData in ADDRESS mode
+ // SetDataUsedSection: Show for WriteData in PARAMETER mode
if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && isByAddress)
{
ColMappingRulesSection.Visibility = Visibility.Visible;
SetDataUsedSection.Visibility = Visibility.Collapsed;
-
+ }
+ else if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && !isByAddress)
+ {
+ ColMappingRulesSection.Visibility = Visibility.Collapsed;
+ SetDataUsedSection.Visibility = Visibility.Visible;
}
else
{
ColMappingRulesSection.Visibility = Visibility.Collapsed;
SetDataUsedSection.Visibility = Visibility.Collapsed;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData ) | |
| { | |
| FillSheetCombo(); | |
| ColMappingRulesSection.Visibility = Visibility.Visible; | |
| } | |
| else | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | |
| } | |
| // 6. Bulk Write Field (ColMappingRules) | |
| // Show ONLY for WriteData in PARAMETER mode. | |
| if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && isByAddress) | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Visible; | |
| SetDataUsedSection.Visibility = Visibility.Collapsed; | |
| } | |
| else | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | |
| SetDataUsedSection.Visibility = Visibility.Collapsed; | |
| } | |
| // 5. Write Value and Bulk Write Fields | |
| // ColMappingRulesSection: Show for WriteData in ADDRESS mode | |
| // SetDataUsedSection: Show for WriteData in PARAMETER mode | |
| if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && isByAddress) | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Visible; | |
| SetDataUsedSection.Visibility = Visibility.Collapsed; | |
| } | |
| else if (mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && !isByAddress) | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | |
| SetDataUsedSection.Visibility = Visibility.Visible; | |
| } | |
| else | |
| { | |
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | |
| SetDataUsedSection.Visibility = Visibility.Collapsed; | |
| } |
🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs around lines
132-153, the first visibility block for ColMappingRulesSection (lines 132-139)
is immediately overwritten by the second block (lines 143-153), creating
dead/conflicting logic; remove the first block and consolidate visibility
handling so ColMappingRulesSection is set only once: Visible when
mAct.ExcelActionType == ActExcel.eExcelActionType.WriteData && isByAddress,
otherwise Collapsed, and ensure SetDataUsedSection.Visibility is set to Visible
when WriteData && not isByAddress and Collapsed in all other cases.
| private void DataSelection_Changed(object sender, RoutedEventArgs e) | ||
| { | ||
| ContextProcessInputValueForDriver(); | ||
|
|
||
| if (ExcelActionComboBox.SelectedValue.ToString() is "ReadData" or "ReadCellData") | ||
|
|
||
| if (rdByAddress == null || rdByParams == null) | ||
| { | ||
| SetDataUsedSection.Visibility = Visibility.Visible; | ||
| ColMappingRulesSection.Visibility = Visibility.Collapsed; | ||
| //fixing for #3811 : to clear Data from Write Data column | ||
| mAct.ColMappingRules = string.Empty; | ||
| //rdByAddress.IsChecked = (mAct.GetInputParamValue(nameof(mAct.DataSelectionMethod)) == "ByCellAddress" ? true : false); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| if (ExcelActionComboBox.SelectedValue.ToString() == "WriteData") | ||
| if ((bool)rdByAddress.IsChecked) | ||
| { | ||
| ColMappingRulesSection.Visibility = Visibility.Visible; | ||
| //mAct.ActInputValues.FirstOrDefault() | ||
| mAct.DataSelectionMethod = ActExcel.eDataSelectionMethod.ByCellAddress; | ||
| rdByParams.IsChecked = false; | ||
| //xAddressInputPanel.Visibility = Visibility.Visible; | ||
| //if (mAct.ExcelActionType is ActExcel.eExcelActionType.WriteData ) | ||
| //{ | ||
| // ColMappingRulesSection.Visibility = Visibility.Visible; | ||
| // SetDataUsedSection.Visibility = Visibility.Collapsed; | ||
|
|
||
| //} | ||
| //else | ||
| //{ | ||
| // ColMappingRulesSection.Visibility = Visibility.Collapsed; | ||
| // SetDataUsedSection.Visibility = Visibility.Collapsed; | ||
|
|
||
| //} | ||
| } | ||
| else | ||
| { | ||
| mAct.DataSelectionMethod = ActExcel.eDataSelectionMethod.ByParameters; | ||
| } | ||
| } | ||
|
|
||
| UpdateVisibility(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove large blocks of commented-out code.
Lines 328-350 contain extensive commented-out code blocks. This reduces readability and should be removed. If the code is needed for reference, use version control history instead.
🔎 Proposed fix
private void DataSelection_Changed(object sender, RoutedEventArgs e)
{
-
if (rdByAddress == null || rdByParams == null)
{
- //rdByAddress.IsChecked = (mAct.GetInputParamValue(nameof(mAct.DataSelectionMethod)) == "ByCellAddress" ? true : false);
-
return;
}
if ((bool)rdByAddress.IsChecked)
{
- //mAct.ActInputValues.FirstOrDefault()
mAct.DataSelectionMethod = ActExcel.eDataSelectionMethod.ByCellAddress;
rdByParams.IsChecked = false;
- //xAddressInputPanel.Visibility = Visibility.Visible;
- //if (mAct.ExcelActionType is ActExcel.eExcelActionType.WriteData )
- //{
- // ColMappingRulesSection.Visibility = Visibility.Visible;
- // SetDataUsedSection.Visibility = Visibility.Collapsed;
-
- //}
- //else
- //{
- // ColMappingRulesSection.Visibility = Visibility.Collapsed;
- // SetDataUsedSection.Visibility = Visibility.Collapsed;
-
- //}
}
else
{
mAct.DataSelectionMethod = ActExcel.eDataSelectionMethod.ByParameters;
}
UpdateVisibility();
}🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActExcelEditPage.xaml.cs around lines
323 to 358, remove the large commented-out blocks between lines ~328-350 to
improve readability — delete unused commented code (the old IsChecked
assignment, visibility toggles and conditional blocks) while keeping the
functional logic intact (retain the null-check, rdByAddress check, assignments
to mAct.DataSelectionMethod and rdByParams.IsChecked, the else branch and the
final UpdateVisibility call); rely on VCS for history instead of inline
commented code.
| else | ||
| { | ||
| AddOrUpdateReturnParamActualWithPath(excelDataTable.Columns[i].ColumnName, r[i].ToString(), (j + 1).ToString() + (i + 1).ToString()); | ||
| } |
There was a problem hiding this comment.
Ambiguous path format causes data collision.
The path (j + 1).ToString() + (i + 1).ToString() creates ambiguous identifiers:
- Row 1, Col 11 →
"111" - Row 11, Col 1 →
"111" - Row 1, Col 1 →
"11", Row 11, Col 11 →"1111"— also collides with Row 1, Col 111
This will cause return parameters to overwrite each other when rows/columns exceed single digits.
🔎 Proposed fix using a separator
else
{
- AddOrUpdateReturnParamActualWithPath(excelDataTable.Columns[i].ColumnName, r[i].ToString(), (j + 1).ToString() + (i + 1).ToString());
+ AddOrUpdateReturnParamActualWithPath(excelDataTable.Columns[i].ColumnName, r[i].ToString(), $"{j + 1}_{i + 1}");
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else | |
| { | |
| AddOrUpdateReturnParamActualWithPath(excelDataTable.Columns[i].ColumnName, r[i].ToString(), (j + 1).ToString() + (i + 1).ToString()); | |
| } | |
| else | |
| { | |
| AddOrUpdateReturnParamActualWithPath(excelDataTable.Columns[i].ColumnName, r[i].ToString(), $"{j + 1}_{i + 1}"); | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/ActExcel.cs around lines 528-531 the code
builds the return-param path by concatenating row and column numbers without a
separator ((j+1).ToString() + (i+1).ToString()), which creates ambiguous
identifiers and collisions for multi-digit indices; change the path construction
to include an unambiguous separator or fixed-width formatting (for example build
the path as (j+1).ToString() + "-" + (i+1).ToString() or use
string.Format/CultureInfo.InvariantCulture with $"{j+1}-{i+1}" or zero-pad each
index) and update any consumers/parsers of these paths to expect the new
delimited format so parameters no longer overwrite each other.
| } | ||
| } | ||
| } | ||
| catch (Exception ex) { Error += eUserMsgKey.ExcelInvalidFieldData + ", " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error while reading cell data from Excel", ex); } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Error assignment uses concatenation and raw enum value.
Using += may append to an existing error, creating confusing messages. Additionally, eUserMsgKey.ExcelInvalidFieldData outputs the enum name rather than a user-friendly message.
🔎 Proposed fix
-catch (Exception ex) { Error += eUserMsgKey.ExcelInvalidFieldData + ", " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error while reading cell data from Excel", ex); }
+catch (Exception ex) { Error = "Invalid field data: " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error while reading cell data from Excel", ex); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| catch (Exception ex) { Error += eUserMsgKey.ExcelInvalidFieldData + ", " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error while reading cell data from Excel", ex); } | |
| catch (Exception ex) { Error = "Invalid field data: " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error while reading cell data from Excel", ex); } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/ActExcel.cs around line 536, the catch block
appends to Error using += and logs the enum name directly; change it to assign
Error = <user-friendly message for eUserMsgKey.ExcelInvalidFieldData> + ", " +
ex.Message (do not use +=) and obtain a readable/localized string for the enum
(e.g., via the existing localization helper or resource lookup) instead of
calling the enum raw; keep the Reporter.ToLog call but consider passing the
composed message and exception for clarity.
| public bool CheckMandatoryFieldsExists(List<string> fields) | ||
| { | ||
| foreach (string field in fields) | ||
| { | ||
| if (String.IsNullOrWhiteSpace((string)this[field])) | ||
| { | ||
| string calculated = "Calculated"; | ||
| int indexOfField = field.IndexOf(calculated); | ||
| var splitBetCapLetters = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); | ||
| string substr = indexOfField != -1 ? field[(indexOfField + calculated.Length)..] : field; | ||
| string actualFieldValue = splitBetCapLetters.Replace(substr, " "); | ||
| this.Error = $"The Mandatory field : {actualFieldValue} cannot be empty"; | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| public object this[string propertyName] { get { return this.GetType().GetProperty(propertyName).GetValue(this, null); } } |
There was a problem hiding this comment.
Unsafe cast and potential NullReferenceException.
Two issues:
-
Line 609: The cast
(string)this[field]will throwInvalidCastExceptionif the property returns a non-string type. For example, if someone adds a non-string property likeRowIndex(currently string but pattern is fragile) to the mandatory check. -
Line 622: The indexer will throw
NullReferenceExceptionifGetProperty(propertyName)returnsnullfor a non-existent property name.
🔎 Proposed fix
public bool CheckMandatoryFieldsExists(List<string> fields)
{
foreach (string field in fields)
{
- if (String.IsNullOrWhiteSpace((string)this[field]))
+ var value = this[field];
+ if (value == null || String.IsNullOrWhiteSpace(value.ToString()))
{
string calculated = "Calculated";
int indexOfField = field.IndexOf(calculated);
var splitBetCapLetters = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
string substr = indexOfField != -1 ? field[(indexOfField + calculated.Length)..] : field;
string actualFieldValue = splitBetCapLetters.Replace(substr, " ");
this.Error = $"The Mandatory field : {actualFieldValue} cannot be empty";
return false;
}
}
return true;
}
-public object this[string propertyName] { get { return this.GetType().GetProperty(propertyName).GetValue(this, null); } }
+public object this[string propertyName]
+{
+ get
+ {
+ var prop = this.GetType().GetProperty(propertyName);
+ return prop?.GetValue(this, null);
+ }
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public bool CheckMandatoryFieldsExists(List<string> fields) | |
| { | |
| foreach (string field in fields) | |
| { | |
| if (String.IsNullOrWhiteSpace((string)this[field])) | |
| { | |
| string calculated = "Calculated"; | |
| int indexOfField = field.IndexOf(calculated); | |
| var splitBetCapLetters = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); | |
| string substr = indexOfField != -1 ? field[(indexOfField + calculated.Length)..] : field; | |
| string actualFieldValue = splitBetCapLetters.Replace(substr, " "); | |
| this.Error = $"The Mandatory field : {actualFieldValue} cannot be empty"; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| public object this[string propertyName] { get { return this.GetType().GetProperty(propertyName).GetValue(this, null); } } | |
| public bool CheckMandatoryFieldsExists(List<string> fields) | |
| { | |
| foreach (string field in fields) | |
| { | |
| var value = this[field]; | |
| if (value == null || String.IsNullOrWhiteSpace(value.ToString())) | |
| { | |
| string calculated = "Calculated"; | |
| int indexOfField = field.IndexOf(calculated); | |
| var splitBetCapLetters = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); | |
| string substr = indexOfField != -1 ? field[(indexOfField + calculated.Length)..] : field; | |
| string actualFieldValue = splitBetCapLetters.Replace(substr, " "); | |
| this.Error = $"The Mandatory field : {actualFieldValue} cannot be empty"; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| public object this[string propertyName] | |
| { | |
| get | |
| { | |
| var prop = this.GetType().GetProperty(propertyName); | |
| return prop?.GetValue(this, null); | |
| } | |
| } |
| public bool WriteCellData(string fileName, string sheetName, string address, string value) | ||
| { | ||
| IWorkbook workbook = null; | ||
|
|
||
| try | ||
| { | ||
|
|
||
| lock (lockObj) | ||
| { | ||
|
|
||
| using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | ||
| { | ||
| if (fileName.EndsWith(".xlsx")) | ||
| { | ||
| workbook = new XSSFWorkbook(fs); | ||
| } | ||
| else if (fileName.EndsWith(".xls")) | ||
| { | ||
| workbook = new HSSFWorkbook(fs); | ||
| } | ||
| } | ||
|
|
||
| if (workbook == null) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to open workbook from file: {fileName}"); | ||
| return false; | ||
| } | ||
|
|
||
| ISheet sheet = workbook.GetSheet(sheetName); | ||
| if (sheet == null) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, $"Sheet '{sheetName}' not found in file: {fileName}"); | ||
| return false; | ||
| } | ||
|
|
||
| CellReference cellRef = new CellReference(address); | ||
| IRow row = sheet.GetRow(cellRef.Row) ?? sheet.CreateRow(cellRef.Row); | ||
| ICell cell = row.GetCell(cellRef.Col) ?? row.CreateCell(cellRef.Col); | ||
|
|
||
| cell.SetCellValue(value); | ||
|
|
||
| sheet.ForceFormulaRecalculation = true; | ||
|
|
||
| using (FileStream fsOut = new FileStream(fileName, FileMode.Create, FileAccess.Write)) | ||
| { | ||
| workbook.Write(fsOut); | ||
| } | ||
|
|
||
| workbook.Close(); | ||
| return true; | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, "Error writing cell data: " + ex.Message, ex); | ||
| return false; | ||
| } | ||
| finally | ||
| { | ||
| workbook?.Close(); | ||
|
|
||
| } | ||
| } |
There was a problem hiding this comment.
Double Close() on workbook in success path.
When the method succeeds, workbook.Close() is called at line 508, and then workbook?.Close() is called again in the finally block at line 519. This results in attempting to close the workbook twice, which could cause issues or exceptions.
Additionally, if the file extension is neither .xlsx nor .xls, the workbook remains null after the using block and the method returns false with an error log, but this case isn't explicitly handled with a clear error message about unsupported format.
🔎 Proposed fix
public bool WriteCellData(string fileName, string sheetName, string address, string value)
{
IWorkbook workbook = null;
try
{
lock (lockObj)
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
if (fileName.EndsWith(".xlsx"))
{
workbook = new XSSFWorkbook(fs);
}
else if (fileName.EndsWith(".xls"))
{
workbook = new HSSFWorkbook(fs);
}
+ else
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Unsupported file format: {fileName}. Only .xlsx and .xls are supported.");
+ return false;
+ }
}
if (workbook == null)
{
Reporter.ToLog(eLogLevel.ERROR, $"Failed to open workbook from file: {fileName}");
return false;
}
ISheet sheet = workbook.GetSheet(sheetName);
if (sheet == null)
{
Reporter.ToLog(eLogLevel.ERROR, $"Sheet '{sheetName}' not found in file: {fileName}");
return false;
}
CellReference cellRef = new CellReference(address);
IRow row = sheet.GetRow(cellRef.Row) ?? sheet.CreateRow(cellRef.Row);
ICell cell = row.GetCell(cellRef.Col) ?? row.CreateCell(cellRef.Col);
cell.SetCellValue(value);
sheet.ForceFormulaRecalculation = true;
using (FileStream fsOut = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
workbook.Write(fsOut);
}
- workbook.Close();
return true;
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.ERROR, "Error writing cell data: " + ex.Message, ex);
return false;
}
finally
{
workbook?.Close();
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public bool WriteCellData(string fileName, string sheetName, string address, string value) | |
| { | |
| IWorkbook workbook = null; | |
| try | |
| { | |
| lock (lockObj) | |
| { | |
| using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | |
| { | |
| if (fileName.EndsWith(".xlsx")) | |
| { | |
| workbook = new XSSFWorkbook(fs); | |
| } | |
| else if (fileName.EndsWith(".xls")) | |
| { | |
| workbook = new HSSFWorkbook(fs); | |
| } | |
| } | |
| if (workbook == null) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to open workbook from file: {fileName}"); | |
| return false; | |
| } | |
| ISheet sheet = workbook.GetSheet(sheetName); | |
| if (sheet == null) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Sheet '{sheetName}' not found in file: {fileName}"); | |
| return false; | |
| } | |
| CellReference cellRef = new CellReference(address); | |
| IRow row = sheet.GetRow(cellRef.Row) ?? sheet.CreateRow(cellRef.Row); | |
| ICell cell = row.GetCell(cellRef.Col) ?? row.CreateCell(cellRef.Col); | |
| cell.SetCellValue(value); | |
| sheet.ForceFormulaRecalculation = true; | |
| using (FileStream fsOut = new FileStream(fileName, FileMode.Create, FileAccess.Write)) | |
| { | |
| workbook.Write(fsOut); | |
| } | |
| workbook.Close(); | |
| return true; | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, "Error writing cell data: " + ex.Message, ex); | |
| return false; | |
| } | |
| finally | |
| { | |
| workbook?.Close(); | |
| } | |
| } | |
| public bool WriteCellData(string fileName, string sheetName, string address, string value) | |
| { | |
| IWorkbook workbook = null; | |
| try | |
| { | |
| lock (lockObj) | |
| { | |
| using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | |
| { | |
| if (fileName.EndsWith(".xlsx")) | |
| { | |
| workbook = new XSSFWorkbook(fs); | |
| } | |
| else if (fileName.EndsWith(".xls")) | |
| { | |
| workbook = new HSSFWorkbook(fs); | |
| } | |
| else | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Unsupported file format: {fileName}. Only .xlsx and .xls are supported."); | |
| return false; | |
| } | |
| } | |
| if (workbook == null) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to open workbook from file: {fileName}"); | |
| return false; | |
| } | |
| ISheet sheet = workbook.GetSheet(sheetName); | |
| if (sheet == null) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Sheet '{sheetName}' not found in file: {fileName}"); | |
| return false; | |
| } | |
| CellReference cellRef = new CellReference(address); | |
| IRow row = sheet.GetRow(cellRef.Row) ?? sheet.CreateRow(cellRef.Row); | |
| ICell cell = row.GetCell(cellRef.Col) ?? row.CreateCell(cellRef.Col); | |
| cell.SetCellValue(value); | |
| sheet.ForceFormulaRecalculation = true; | |
| using (FileStream fsOut = new FileStream(fileName, FileMode.Create, FileAccess.Write)) | |
| { | |
| workbook.Write(fsOut); | |
| } | |
| return true; | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, "Error writing cell data: " + ex.Message, ex); | |
| return false; | |
| } | |
| finally | |
| { | |
| workbook?.Close(); | |
| } | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Run/Excels/ExcelNPOIOperations.cs around lines 460-522,
the workbook is closed twice on the success path (once before returning and
again in the finally block) and unsupported file extensions are not explicitly
reported; remove the explicit workbook.Close() call in the try-success path and
let the finally block handle disposal, and add a clear error log and early
return when the file extension is neither .xlsx nor .xls (i.e., detect
unsupported format after opening FileStream and before using the workbook), so
the method logs a descriptive unsupported-format error and returns false instead
of proceeding with a null workbook.
| </PackageReference> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> | ||
| <PackageReference Include="Microsoft.NETCore.App" Version="2.1.30" /> | ||
| <PackageReference Include="Microsoft.Office.Interop.Excel" Version="16.0.18925.20022" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the target framework in GingerCoreTest.csproj
echo "=== Target Framework in GingerCoreTest.csproj ==="
grep -E "TargetFramework|TargetFrameworks" Ginger/GingerCoreTest/GingerCoreTest.csproj
echo -e "\n=== Search for Excel Interop usage in tests ==="
rg -t cs "Microsoft\.Office\.Interop\.Excel|Excel\.Application|Excel\.Workbook" --max-count=20
echo -e "\n=== Search for COM cleanup patterns (Marshal.ReleaseComObject) ==="
rg -t cs "Marshal\.ReleaseComObject|Marshal\.FinalReleaseComObject" --max-count=20
echo -e "\n=== Context around line 66 in GingerCoreTest.csproj ==="
head -n 70 Ginger/GingerCoreTest/GingerCoreTest.csproj | tail -n 15Repository: Ginger-Automation/Ginger
Length of output: 1267
🏁 Script executed:
#!/bin/bash
# Check the target framework in GingerCoreTest.csproj
echo "=== Target Framework in GingerCoreTest.csproj ==="
grep -E "TargetFramework|TargetFrameworks" Ginger/GingerCoreTest/GingerCoreTest.csproj
echo -e "\n=== Search for Excel Interop usage in tests ==="
rg -t cs "Microsoft\.Office\.Interop\.Excel|Excel\.Application|Excel\.Workbook" --max-count=20
echo -e "\n=== Search for COM cleanup patterns (Marshal.ReleaseComObject) ==="
rg -t cs "Marshal\.ReleaseComObject|Marshal\.FinalReleaseComObject" --max-count=20
echo -e "\n=== Context around line 66 in GingerCoreTest.csproj ==="
head -n 70 Ginger/GingerCoreTest/GingerCoreTest.csproj | tail -n 15Repository: Ginger-Automation/Ginger
Length of output: 1267
🏁 Script executed:
# First verify the file exists and check target framework
if [ -f "Ginger/GingerCoreTest/GingerCoreTest.csproj" ]; then
echo "=== Target Framework in GingerCoreTest.csproj ==="
grep -E "TargetFramework" Ginger/GingerCoreTest/GingerCoreTest.csproj | head -3
else
echo "File not found. Searching for .csproj files..."
find . -name "GingerCoreTest.csproj" -type f 2>/dev/null
fi
echo -e "\n=== Searching for Excel Interop usage ==="
rg "Microsoft\.Office\.Interop\.Excel|Excel\.Application" -t cs 2>/dev/null | head -10 || echo "No Excel Interop usage found"
echo -e "\n=== Searching for COM cleanup patterns ==="
rg "Marshal\.ReleaseComObject|Marshal\.FinalReleaseComObject" -t cs 2>/dev/null | head -10 || echo "No COM cleanup found"Repository: Ginger-Automation/Ginger
Length of output: 476
🏁 Script executed:
if [ -f "Ginger/GingerCoreTest/GingerCoreTest.csproj" ]; then
echo "=== Target Framework ==="
grep "TargetFramework" Ginger/GingerCoreTest/GingerCoreTest.csproj
echo -e "\n=== Around line 66 ==="
sed -n '60,72p' Ginger/GingerCoreTest/GingerCoreTest.csproj
else
find . -name "*GingerCoreTest*" -type f 2>/dev/null | head -5
fiRepository: Ginger-Automation/Ginger
Length of output: 858
Remove Microsoft.Office.Interop.Excel from GingerCoreTest.csproj or verify if Excel automation is actually required.
COM add-ins cannot be created with .NET Core and .NET 5+, which may lead to add-in load failures, making this package incompatible with the .NET 8 target framework. Codebase search found no actual Excel Interop usage in the GingerCoreTest project—only a comment reference in the main project.
If Excel automation is genuinely needed for tests, move the package reference to a separate .NET Framework 4.8+ project. The package is entirely unsupported with no license since it is a repackaging of Office assemblies.
🤖 Prompt for AI Agents
In Ginger/GingerCoreTest/GingerCoreTest.csproj at line 66, the project
references Microsoft.Office.Interop.Excel which is incompatible with .NET 8 and
appears unused in this test project; remove the PackageReference from
GingerCoreTest.csproj. If Excel automation is required for tests, create/move to
a separate .NET Framework 4.8+ test project and add the Interop package there
instead; otherwise delete the reference and verify tests/builds succeed and no
compile/runtime references remain.
Description
Type of Change
Checklist
[IsSerializedForLocalRepository]where neededReporter.ToLog()patternSummary by CodeRabbit
Release Notes
New Features
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.