@@ -111,4 +111,186 @@ public void List_WorkbookWithManualTable_ReturnsOnlyQueries()
111111 // Query should be connection-only (manual table shouldn't affect this)
112112 Assert . True ( query . IsConnectionOnly ) ;
113113 }
114+
115+ /// <summary>
116+ /// Regression test: Verifies View() handles workbooks with manually created tables
117+ /// Bug: View() was throwing COMException 0x800A03EC when iterating ListObjects
118+ /// because manually created tables don't have QueryTable property.
119+ /// Fix: View() now catches COMException when accessing ListObject.QueryTable and skips non-query tables.
120+ /// </summary>
121+ [ Fact ]
122+ public void View_WorkbookWithManualTable_ReturnsQueryDetails ( )
123+ {
124+ // Arrange
125+ var testFile = _fixture . CreateTestFile ( ) ;
126+
127+ var dataModelCommands = new DataModelCommands ( ) ;
128+ var commands = new PowerQueryCommands ( dataModelCommands ) ;
129+
130+ const string queryName = "TestQuery" ;
131+ const string mCode = @"let
132+ Source = #table(
133+ {""Column1"", ""Column2""},
134+ {
135+ {""A"", ""B""},
136+ {""C"", ""D""}
137+ }
138+ )
139+ in
140+ Source" ;
141+
142+ // Act
143+ using var batch = ExcelSession . BeginBatch ( testFile ) ;
144+
145+ // Step 1: Create a manually created table (no Power Query connection)
146+ batch . Execute ( ( ctx , ct ) =>
147+ {
148+ dynamic ? sheet = null ;
149+ dynamic ? range = null ;
150+ dynamic ? listObjects = null ;
151+ try
152+ {
153+ sheet = ctx . Book . Worksheets . Item ( 1 ) ;
154+ sheet . Name = "TestSheet" ;
155+
156+ // Add some data
157+ range = sheet . Range [ "A1:B3" ] ;
158+ range . Value2 = new object [ , ]
159+ {
160+ { "Header1" , "Header2" } ,
161+ { "Data1" , "Data2" } ,
162+ { "Data3" , "Data4" }
163+ } ;
164+
165+ // Create a manual table (ListObject) - NO QueryTable
166+ listObjects = sheet . ListObjects ;
167+ dynamic ? listObject = listObjects . Add (
168+ 1 , // xlSrcRange (manual table from range)
169+ range , // Source range
170+ Type . Missing , // LinkSource
171+ 1 , // xlYes (has headers)
172+ Type . Missing // Destination
173+ ) ;
174+ listObject . Name = "ManualTable" ;
175+ ComUtilities . Release ( ref listObject ! ) ;
176+ }
177+ finally
178+ {
179+ ComUtilities . Release ( ref listObjects ! ) ;
180+ ComUtilities . Release ( ref range ! ) ;
181+ ComUtilities . Release ( ref sheet ! ) ;
182+ }
183+ } ) ;
184+
185+ // Step 2: Create a Power Query (connection-only)
186+ commands . Create ( batch , queryName , mCode , PowerQueryLoadMode . ConnectionOnly ) ;
187+
188+ // Step 3: View the query - THIS WAS THROWING 0x800A03EC before the fix
189+ var result = commands . View ( batch , queryName ) ;
190+
191+ // Assert
192+ Assert . True ( result . Success , $ "View failed: { result . ErrorMessage } ") ;
193+ Assert . Equal ( queryName , result . QueryName ) ;
194+ Assert . NotEmpty ( result . MCode ) ;
195+ Assert . Contains ( "Source = #table" , result . MCode ) ;
196+
197+ // Verify load destination detected correctly despite manual table presence
198+ Assert . True ( result . IsConnectionOnly ) ;
199+ }
200+
201+ /// <summary>
202+ /// Regression test: Verifies Update() handles workbooks with manually created tables
203+ /// Bug: Update() was throwing COMException 0x800A03EC when iterating ListObjects
204+ /// because manually created tables don't have QueryTable property.
205+ /// Fix: Update() now catches COMException when accessing ListObject.QueryTable and skips non-query tables.
206+ /// </summary>
207+ [ Fact ]
208+ public void Update_WorkbookWithManualTable_UpdatesQuerySuccessfully ( )
209+ {
210+ // Arrange
211+ var testFile = _fixture . CreateTestFile ( ) ;
212+
213+ var dataModelCommands = new DataModelCommands ( ) ;
214+ var commands = new PowerQueryCommands ( dataModelCommands ) ;
215+
216+ const string queryName = "TestQuery" ;
217+ const string originalMCode = @"let
218+ Source = #table(
219+ {""Column1"", ""Column2""},
220+ {
221+ {""A"", ""B""},
222+ {""C"", ""D""}
223+ }
224+ )
225+ in
226+ Source" ;
227+
228+ const string updatedMCode = @"let
229+ Source = #table(
230+ {""NewCol1"", ""NewCol2"", ""NewCol3""},
231+ {
232+ {1, 2, 3},
233+ {4, 5, 6}
234+ }
235+ )
236+ in
237+ Source" ;
238+
239+ // Act
240+ using var batch = ExcelSession . BeginBatch ( testFile ) ;
241+
242+ // Step 1: Create a manually created table (no Power Query connection)
243+ batch . Execute ( ( ctx , ct ) =>
244+ {
245+ dynamic ? sheet = null ;
246+ dynamic ? range = null ;
247+ dynamic ? listObjects = null ;
248+ try
249+ {
250+ sheet = ctx . Book . Worksheets . Item ( 1 ) ;
251+ sheet . Name = "TestSheet" ;
252+
253+ // Add some data
254+ range = sheet . Range [ "A1:B3" ] ;
255+ range . Value2 = new object [ , ]
256+ {
257+ { "Header1" , "Header2" } ,
258+ { "Data1" , "Data2" } ,
259+ { "Data3" , "Data4" }
260+ } ;
261+
262+ // Create a manual table (ListObject) - NO QueryTable
263+ listObjects = sheet . ListObjects ;
264+ dynamic ? listObject = listObjects . Add (
265+ 1 , // xlSrcRange (manual table from range)
266+ range , // Source range
267+ Type . Missing , // LinkSource
268+ 1 , // xlYes (has headers)
269+ Type . Missing // Destination
270+ ) ;
271+ listObject . Name = "ManualTable" ;
272+ ComUtilities . Release ( ref listObject ! ) ;
273+ }
274+ finally
275+ {
276+ ComUtilities . Release ( ref listObjects ! ) ;
277+ ComUtilities . Release ( ref range ! ) ;
278+ ComUtilities . Release ( ref sheet ! ) ;
279+ }
280+ } ) ;
281+
282+ // Step 2: Create a Power Query (connection-only)
283+ commands . Create ( batch , queryName , originalMCode , PowerQueryLoadMode . ConnectionOnly ) ;
284+
285+ // Step 3: Update the query - THIS WAS THROWING 0x800A03EC before the fix
286+ commands . Update ( batch , queryName , updatedMCode ) ;
287+
288+ // Step 4: Verify the update by viewing
289+ var viewResult = commands . View ( batch , queryName ) ;
290+ Assert . True ( viewResult . Success , $ "View after update failed: { viewResult . ErrorMessage } ") ;
291+ Assert . Contains ( "NewCol1" , viewResult . MCode ) ;
292+ Assert . Contains ( "NewCol2" , viewResult . MCode ) ;
293+ Assert . Contains ( "NewCol3" , viewResult . MCode ) ;
294+ Assert . DoesNotContain ( "Column1" , viewResult . MCode ) ;
295+ }
114296}
0 commit comments