PageUnitGUI() {
		final JFrame jf = new JFrame("PageUnit Runner");
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final Container cp = new JPanel();
		jf.add(cp, BorderLayout.CENTER);
		
		cp.setLayout(new FlowLayout());
		cp.add(new JLabel("PageUnit Test File:"));
		final JTextField tf = new JTextField(20);
		cp.add(tf);
		
		String rememberedCurrentDirectory = p.get("directory", null);
		if (rememberedCurrentDirectory != null) {
			fileChooser.setCurrentDirectory(new File(rememberedCurrentDirectory));
		}

		final JButton browseButton = new JButton("...");
		cp.add(browseButton);
		browseButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int ret = fileChooser.showOpenDialog(tf);
				// blocking wait
				// XXX should use invokeLater()?
				if (ret != JFileChooser.APPROVE_OPTION) {
					return;
				}
				p.put("directory", fileChooser.getCurrentDirectory().getAbsolutePath());
				tf.setText(fileChooser.getSelectedFile().getAbsolutePath());
			}		
		});

		final JButton testButton = new JButton("Test");
		cp.add(testButton);
		testButton.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent evt) {
				final String fileName = tf.getText();
				if (fileName == null || fileName.length() == 0) {
					return;
				}
				final File f = new File(fileName);
				if (!f.canRead()) {
					error(jf, "Can't read file " + f);
				}
				
				// Run rest of this under a new Thread, so we don't block the EventDispatch thread...
				new Thread() {
					public void run() {
				
						try {
							TestResult results = new TestResult() {
								@Override
								public void startTest(Test test) {
									super.startTest(test);
									crank();
								}
								@Override
								public synchronized void addError(Test test, Throwable t) {
									super.addError(test, t);
									seeRed();
								}
								@Override
								public synchronized void addFailure(Test test, AssertionFailedError t) {
									super.addFailure(test, t);
									seeRed();
								}
							};
							TestCase t = new ScriptTestCase(f.getAbsolutePath());
							int max = t.countTestCases();
							System.out.printf("Starting %d tests%n", max);
							bar.setMaximum(max);
							t.run(results);
						} catch (Exception e) {
							error(jf, e.toString());
						}
					}
				}.start();
				
			}			
		});	
		
		jf.add(bar, BorderLayout.SOUTH);
		seeGreen();
		
		jf.pack();
		UtilGUI.monitorWindowPosition(jf, p);
		jf.setVisible(true);
	}