We have a PDF user manual that can be accessed from the desktop application under test.
I need to test if the PDF has actually successfully opened in the associated PDF viewer. I don’t want to assume the user has a specific PDF viewer like Adobe Reader.
Any ideas on how to determine which PDF viewer is associated to PDF files, and if it’s been opened successfully with a specific PDF?
As for …“if it’s been opened successfully with a specific PDF?” You could check the object tree for the viewer process with some code after you retrieve the associated viewer. It may take some clever code to check for the various viewers but it should be possible. Opening via a browser would be a different process as well.
… If you find my posts helpful drop me a like! Be sure to mark or post the solution to help others out and/or to credit the one who helped you.
This part is a bit confusing from an automation test perspective,
I need to test if the PDF has actually successfully opened in the associated PDF viewer. I don’t want to assume the user has a specific PDF viewer like Adobe Reader.
I am not sure how this could be done unless you check every possible reader. A developer could build an error check into the application under test but I don’t know how you would test this.
(P.S. Maybe you are spinning up various test environments multiple VMs? )
This code should open the file in the specified path and you can use the registry code to determine the viewer to check for…
function OpenAndVerifyPDF(pdfPath) {
var shell = Sys.OleObject("WScript.Shell");
shell.Run('"' + pdfPath + '"', 1, false); // Launch PDF with default viewer
// Wait a few seconds for the viewer to open
Delay(3000);
// Check if the Adobe Reader or another viewer process is running
if (Sys.WaitProcess("AcroRd32", 5000).Exists || Sys.WaitProcess("Acrobat", 5000).Exists) {
Log.Message("PDF viewer opened successfully.");
return true;
} else {
Log.Error("PDF viewer did not open.");
return false;
}
}
P.S. The bit of code in this example that checks the tree for the running process could be useful after your app has opened the file. You will need to check the associated process and window caption for the filename if available.
PDF files are typically associated with a reader in Windows (and of course, that association is saved in the Windows Registry). I.e. we don’t need to check every possible reader; Windows already knows.
In .NET, when you create a new process, there’s a flag called “UseShellExecute” that tells it to auto-magically find and call the associated application for the specified file (in this case, a PDF file).
So, like I mentioned, I was hoping for a similar “shortcut” in TestComplete (i.e. “Hey, go find the associated application and tell me it’s open.”). But since I guess there isn’t, as rraghvani mentioned, I’ll need to manually dig into the registry to find the file association and then check if that associated process is running.
Thanks. The PDF is opened by the application under test, so it should already be open; I just need to make sure it’s open. Looks like the second part after the Delay() shows how to do that…
The tech article I referenced should help you find the reader process to substitute at line 9. It sounds like you have it. The reader should be known in your test environment. However, flexible tests are always good.
Originally posted by Hassan_Ballan on July 09, 2025
After your application under test launches the required PDF file and to verify that the file is opened correctly in its associated application even when another instance of the viewer might already be running or in Browser / Embedded viewers.
This can be achieved by reading the Windows Registry to determine the default PDF viewer, and checking window titles for the expected file name within a 10-second timeout.
I have not tried it out but here’s a TestComplete example in JavaScript:
function VerifyPDF(pdfFilePath) {
if (!aqFileSystem.Exists(pdfFilePath)) {
Log.Error("The specified PDF file does not exist: " + pdfFilePath);
return;
}
var fileName = aqFileSystem.GetFileName(pdfFilePath).toLowerCase();
var fileBase = fileName.replace(/\.[^/.]+$/, "");
// === STEP 1: Determine default viewer from registry
var exeName = null;
var useFallback = false;
var progID = aqRegistry.GetRegValue("HKEY_CLASSES_ROOT\\.pdf", "");
if (progID) {
var commandPath = "HKEY_CLASSES_ROOT\\" + progID + "\\shell\\open\\command\\";
var openCommand = aqRegistry.GetRegValue(commandPath, "");
var exeMatch = openCommand.match(/"?(.*?\.exe)"/i);
var exePath = exeMatch ? exeMatch[1] : null;
exeName = exePath ? aqFileSystem.GetFileName(exePath).toLowerCase() : null;
}
// === STEP 2: Define fallback process list (common browsers and embedded containers)
var fallbackProcesses = [
"msedge", "chrome", "firefox", "iexplore", // browsers
"electron", "yourapp", "customviewer" // embedded apps (customize here)
];
if (!exeName) {
Log.Warning("Could not detect default PDF viewer from registry. Falling back to known browser/embedded types.");
useFallback = true;
}
var timeout = 10000; // 10 seconds
var start = aqDateTime.GetTickCount();
var found = false;
var anyViewerFound = false;
while ((aqDateTime.GetTickCount() - start) < timeout) {
for (var i = 0; i < Sys.ProcessCount; i++) {
var proc = Sys.Process(i);
var procName = proc.Name.toLowerCase();
// Match default viewer or fallback list
var isTargetViewer = (!useFallback && procName === exeName.replace(".exe", "")) ||
(useFallback && fallbackProcesses.indexOf(procName) >= 0);
if (!isTargetViewer) continue;
anyViewerFound = true;
// === Check top-level windows
for (var j = 0; j < proc.WindowCount; j++) {
var wnd = proc.Window("*", "*", j);
if (!wnd.Exists) continue;
var title = wnd.WndCaption.toLowerCase();
// For browsers, title may show full path or filename
if (title.includes(fileBase) || title.includes(fileName)) {
Log.Message("PDF file '" + fileName + "' is opened in: " + procName + " - Window: " + wnd.WndCaption);
found = true;
break;
}
}
if (found) break;
}
if (found) break;
Delay(500);
}
// === Final result
if (!found) {
if (!anyViewerFound) {
Log.Error("No PDF viewer process (application or browser) was started.");
} else {
Log.Warning("Viewer is running, but no window was found showing the PDF file: " + fileName);
}
}
}
If a response helped you out, don’t forget to Like it! And if it answered your question, mark it as the solution so others can benefit too.
In case it’s helpful, here’s what I came up with. This only works if the associated PDF app is initially opened with the PDF file; if the app is already open, it gets much harder to check. (Chrome and Edge in particular are difficult to check since it’s highly likely they are already open.)
import winreg
import shlex
class Registry:
@staticmethod
def ReadValue( root, keyPath, valueName ):
try:
with winreg.OpenKey( root, keyPath, 0, winreg.KEY_READ ) as key:
value, _ = winreg.QueryValueEx( key, valueName )
return value
except FileNotFoundError:
Log.Error( fr"Registry key or value not found: \{keyPath}\{valueName}" )
except Exception as e:
Log.Error( f"Exception: {e}" )
return
@staticmethod
def DoesProgIdKeyExist( progId ):
try:
return winreg.OpenKey( winreg.HKEY_CLASSES_ROOT, progId ) != ""
except FileNotFoundError:
Log.Error( f"ProgId '{progId}' key not found." )
except Exception as e:
Log.Error( f"Exception: {e}" )
return
@staticmethod
def GetAppOpenCommandPath( progIdKey ):
try:
keyValue = Registry.ReadValue( winreg.HKEY_CLASSES_ROOT, fr"{progIdKey}\shell\open\command", "" )
exePath = shlex.split( keyValue )[0]
return exePath
except FileNotFoundError:
Log.Error( f"App open command key not found for key '{progIdKey}'." )
except Exception as e:
Log.Error( f"Exception: {e}" )
return
# end Registry class
def GetProcessName( exePath ):
startIndex = exePath.rindex( '\\' ) + 1
endIndex = exePath.rindex( '.' )
processName = exePath[startIndex:endIndex]
return processName
def CheckProcessForPdfFile( pdfProcessName, pdfFileName ):
result = False
processCount = Sys.ChildCount
for index in range( 0, processCount - 1 ):
process = Sys.Child( index )
if pdfProcessName == process.ProcessName:
#Log.Message( f"PDF process '{process.ProcessName}' found..." )
if pdfFileName.lower() in process.CommandLine.lower():
Log.Message( "PDF file in command line.")
result = True
break
return result
def Example():
pdfExtPath = r"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice"
pdfExtValue = "ProgId"
progId = Registry.ReadValue(winreg.HKEY_CURRENT_USER, pdfExtPath, pdfExtValue)
Log.Message(f"Prog ID = {progId}")
exePath = Registry.GetAppOpenCommandPath( progId ) if Registry.DoesProgIdKeyExist( progId ) else ""
Log.Message( f"App open command path = {exePath}")
pdfProcessName = GetProcessName( exePath )
thePdfFileName = "User-Manual.pdf"
if Sys.WaitProcess( pdfProcessName, 5000 ).Exists:
Log.Checkpoint( f"PDF viewer '{pdfProcessName}' is open." )
if CheckProcessForPdfFile( pdfProcessName, thePdfFileName ):
Log.Checkpoint( f"PDF viewer has opened the expected PDF file '{thePdfFileName}'." )
else:
Log.Warning( f"The expected PDF file '{thePdfFileName}' might be open, but can't determine for sure." )
else:
Log.Error( "PDF viewer is not open." )