From df2befac4e294bc943355950455fdff9f3caec04 Mon Sep 17 00:00:00 2001 From: CVS To Bazaar Migration Date: Tue, 28 Jul 2009 16:10:59 +0000 Subject: [PATCH 01/13] This commit was manufactured by cvs2bzr to create the 'TESTOPIA_2_2_BUGZILLA_3_2' tag. Cherrypick from TESTOPIA-2_2-BRANCH-BUGZILLA-3_2 2009-07-28 16:10:57 UTC CVS To Bazaar Migration 'This commit was manufactured by cvs2bzr to create the 'TESTOPIA-2_2-BRANCH-': .project testopia/img/RUNNING_gray.gif template/en/default/testopia/tag/list.html.tmpl tr_list_cases.cgi template/en/default/hook/global/header.html.tmpl/additional_header/testopia_styles.html.tmpl Bugzilla/Config/Testopia.pm template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl testopia/img/RUNNING_small.gif testopia.dtd template/en/default/testopia/environment/table.html.tmpl template/en/default/testopia/tag/show.html.tmpl Bugzilla/Testopia/Attachment.pm testopia/img/Testopia_pocket.eps tr_admin.cgi template/en/default/testopia/tag/table.html.tmpl template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl testopia/img/add.png tr_attachment.cgi testopia/img/add.svg testopia/ChangeLog Bugzilla/Testopia/Build.pm testopia/img/archived.png template/en/default/hook/index.html.tmpl/links/tr.html.tmpl testopia/img/archived.svg testopia/README testopia/img/aw.gif tr_builds.cgi testopia/t/API_Build.pm testopia/t/API_Environment.pm Bugzilla/Testopia/Category.pm testopia/environment.dtd template/en/default/hook/list/list.html.tmpl/links/tr.html.tmpl testopia/t/API_Product.pm testopia/img/awg.gif testopia/t/API_Suite.pm template/en/default/testopia/export/csv.case.tmpl testopia/t/API_TestCase.pm tr_case_reports.cgi testopia/import_example.csv testopia/img/cancel.gif template/en/default/testopia/export/csv.caseheader.tmpl Bugzilla/Testopia/Classification.pm testopia/img/category.gif testopia/t/API_TestPlan.pm testopia/img/circle.gif template/en/default/testopia/export/xml.case.tmpl testopia/t/API_TestRun.pm template/en/default/testopia/messages.html.tmpl Bugzilla/Testopia/Constants.pm testopia/img/circle.png template/en/default/testopia/export/xml.footer.tmpl tr_caserun_reports.cgi testopia/t/OBJ_Attachment.pm testopia/img/city.eps template/en/default/testopia/export/xml.header.tmpl testopia/t/OBJ_Builds.pm tr_categories.cgi testopia/t/OBJ_Builds_GREGG.pm template/en/default/testopia/import/importer.html.tmpl testopia/img/confirm.png testopia/t/OBJ_Category.pm template/en/default/testopia/quicksearch.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/TestPlan.html template/en/default/testopia/plan/access-list.html.tmpl testopia/t/OBJ_Environment.pm testopia/img/confirm.svg template/en/default/testopia/plan/add.html.tmpl testopia/t/OBJ_TestCase.pm testopia/patch-3.2 tr_csv2xml.pl Bugzilla/Testopia/Product.pm testopia/t/SE_attachments.t template/en/default/testopia/style.none.tmpl testopia/img/copy.gif testopia/run_unit_tests.pl testopia/t/SE_builds.t template/en/default/testopia/plan/choose.html.tmpl testopia/img/copy.png Bugzilla/WebService/Testopia/Environment.pm testopia/t/SE_caseruns.t tr_draw.cgi testopia/img/copy.svg template/en/default/testopia/text.png.tmpl testopia/t/SE_cases.t testopia/t/SE_categories.t tr_environments.cgi testopia/t/SE_environments.t Bugzilla/Testopia/Report.pm template/en/default/testopia/admin/show.html.tmpl testopia/t/SE_plans.t template/en/default/testopia/case/add.html.tmpl tr_export_environment.cgi testopia/t/SE_query.t template/en/default/testopia/admin/plantypes/add.html.tmpl testopia/t/SE_runs.t testopia/img/delete.svg template/en/default/testopia/plan/diff_doc.html.tmpl tr_history.cgi testopia/t/SE_tags.t testopia/img/edit.png testopia/t/Test.pm testopia/img/edit.svg tr_import_environment.cgi testopia/t/Testopia/Test/Constants.pm testopia/img/element.gif template/en/default/testopia/plan/history.html.tmpl testopia/t/Testopia/Test/Util.pm template/en/default/testopia/admin/plantypes/edit.html.tmpl testopia/img/env_lookup.png testopia/t/Testopia/Test/API/Constants.pm tr_importer.cgi testopia/t/Testopia/Test/API/Util.pm template/en/default/testopia/admin/plantypes/show.html.tmpl testopia/img/env_lookup.svg testopia/t/Testopia/Test/Selenium/Constants.pm tr_importxml.pl testopia/t/Testopia/Test/Selenium/Util.pm template/en/default/testopia/plan/list.html.tmpl Bugzilla/Testopia/Table.pm template/en/default/testopia/attachment/choose.html.tmpl testopia/testrunner.pl testopia/t/data/dummy_load.sql testopia/img/folder.gif testopia/contrib/cvs_update.sh testopia/tools/readme tr_list_caseruns.cgi template/en/default/testopia/caserun/list.json.tmpl testopia/img/folder_blue.gif template/en/default/testopia/plan/list.json.tmpl testopia/contrib/drivers/java/.classpath Bugzilla/Testopia/TestCase.pm template/en/default/testopia/plan/navigate.html.tmpl template/en/default/testopia/attachment/list.json.tmpl testopia/contrib/drivers/java/.project testopia/img/folder_red.gif testopia/contrib/drivers/java/OptionalData.xml testopia/img/go.png testopia/contrib/drivers/java/TESTS-TestSuites.xml testopia/img/delete.png template/en/default/testopia/plan/show.csv.tmpl testopia/img/IDLE_small.gif testopia/img/go.svg testopia/contrib/drivers/java/build.xml testopia/img/green_bar.gif testopia/contrib/drivers/java/testopiaData.xml template/en/default/testopia/plan/show.html.tmpl testopia/contrib/drivers/java/doc/allclasses-frame.html testopia/img/link.png testopia/contrib/drivers/java/doc/allclasses-noframe.html testopia/img/link.svg testopia/contrib/drivers/java/doc/constant-values.html testopia/img/lk.gif testopia/contrib/drivers/java/doc/deprecated-list.html template/en/default/testopia/reports/report-bar.png.tmpl testopia/contrib/drivers/java/doc/help-doc.html testopia/img/new.png testopia/contrib/drivers/java/doc/index.html template/en/default/testopia/plan/show.print.tmpl tr_list_environments.cgi testopia/img/new.svg testopia/contrib/drivers/java/doc/overview-tree.html testopia/img/orange_bar.gif template/en/default/testopia/plan/show.xml.tmpl testopia/contrib/drivers/java/doc/package-list testopia/contrib/drivers/java/doc/stylesheet.css testopia/img/pb.gif testopia/contrib/drivers/java/doc/index-files/index-1.html Bugzilla/Testopia/TestPlan.pm testopia/contrib/drivers/java/doc/index-files/index-10.html template/en/default/testopia/product/dashboard.html.tmpl template/en/default/testopia/case/choose.html.tmpl testopia/img/pg.gif testopia/contrib/drivers/java/doc/index-files/index-11.html template/en/default/testopia/product/show.html.tmpl tr_list_runs.cgi testopia/contrib/drivers/java/doc/index-files/index-2.html testopia/img/product.gif testopia/contrib/drivers/java/doc/index-files/index-3.html template/en/default/testopia/reports/bug-count.html.tmpl template/en/default/testopia/admin/plantypes/delete.html.tmpl testopia/img/property.gif testopia/contrib/drivers/java/doc/index-files/index-4.html testopia/img/red_bar.gif testopia/contrib/drivers/java/doc/index-files/index-5.html template/en/default/testopia/reports/bug-severity-status-form.html.tmpl testopia/contrib/drivers/java/doc/index-files/index-6.html Bugzilla/Testopia/TestRun.pm template/en/default/testopia/case/filter.html.tmpl testopia/contrib/drivers/java/doc/index-files/index-7.html testopia/img/refresh.svg template/en/default/testopia/reports/bug-stats-form.html.tmpl testopia/contrib/drivers/java/doc/index-files/index-8.html testopia/img/save.png testopia/contrib/drivers/java/doc/index-files/index-9.html testopia/img/save.svg testopia/contrib/drivers/java/doc/resources/inherit.gif testopia/img/saw.gif template/en/default/testopia/reports/build-coverage.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/Build.html tr_new_environment.cgi testopia/contrib/drivers/java/doc/testopia/API/class-use/Build.html template/en/default/testopia/case/history.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/Component.html testopia/img/selected_value.png template/en/default/testopia/reports/completion.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/Environment.html Bugzilla/Testopia/TestTag.pm tr_new_plan.cgi testopia/js/strings.js testopia/contrib/drivers/java/doc/testopia/API/Product.html template/en/default/testopia/case/list.csv.tmpl template/en/default/testopia/reports/completion.png.tmpl testopia/contrib/drivers/java/testopia/Test/ParseAnt.java testopia/contrib/drivers/java/doc/testopia/API/TestAPI.html testopia/img/square.gif template/en/default/testopia/reports/execution.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/TestCaseRun.html template/en/default/testopia/case/list.html.tmpl template/en/default/testopia/reports/header.html.tmpl Bugzilla/Testopia/Util.pm testopia/img/square.png tr_new_run.cgi testopia/contrib/drivers/java/doc/testopia/API/TestRun.html testopia/img/stop.png testopia/contrib/drivers/java/doc/testopia/API/TestopiaTestCase.html Bugzilla/Testopia/Xml.pm testopia/img/stop.svg testopia/contrib/drivers/java/doc/testopia/API/User.html testopia/img/td.gif template/en/default/testopia/case/list.json.tmpl testopia/contrib/drivers/java/doc/testopia/API/package-frame.html template/en/default/testopia/reports/priority-breakdown.html.tmpl template/en/default/testopia/case/list.xml.tmpl testopia/contrib/drivers/java/doc/testopia/API/package-summary.html tr_plan_access.cgi Bugzilla/Testopia/XmlReferences.pm testopia/img/testopia_big_picture.png testopia/contrib/drivers/java/doc/testopia/API/package-tree.html template/en/default/testopia/case/navigate.html.tmpl template/en/default/testopia/reports/report-line.png.tmpl testopia/contrib/drivers/java/doc/testopia/API/package-use.html testopia/img/testopia_city_256.png Bugzilla/Testopia/XmlTestCase.pm tr_plan_reports.cgi testopia/img/testopia_city_128.png template/en/default/testopia/reports/report-pie.png.tmpl template/en/default/testopia/case/new-bug.xml.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/Component.html template/en/default/testopia/case/show.csv.tmpl template/en/default/testopia/reports/report-table.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/Environment.html Bugzilla/Testopia/Environment/Category.pm testopia/contrib/drivers/java/doc/testopia/API/class-use/Product.html tr_process_case.cgi template/en/default/testopia/reports/report.csv.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/TestAPI.html testopia/img/testopia_city_512.png testopia/doc/training.odp Bugzilla/Testopia/Environment/Element.pm testopia/contrib/drivers/java/doc/testopia/API/class-use/TestCaseRun.html template/en/default/testopia/reports/report.html.tmpl template/en/default/testopia/blocks.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/TestPlan.html testopia/img/testopia_logo_128.png testopia/contrib/drivers/java/doc/testopia/API/class-use/TestRun.html template/en/default/testopia/reports/status.html.tmpl Bugzilla/Testopia/Environment/Property.pm template/en/default/testopia/reports/test-burnout-form.html.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/TestopiaTestCase.html testopia/img/testopia_logo_256.png template/en/default/testopia/case/show.print.tmpl testopia/contrib/drivers/java/doc/testopia/API/class-use/User.html Bugzilla/Testopia/Environment/Xml.pm testopia/contrib/drivers/java/jar/README template/en/default/testopia/reports/untested.html.tmpl template/en/default/testopia/case/show.xml.tmpl testopia/t/API_TestCaseRun.pm testopia/contrib/drivers/java/testopia/API/Build.java template/en/default/testopia/run/add.html.tmpl tr_product_reports.cgi testopia/contrib/drivers/java/testopia/API/Component.java testopia/img/triangle.gif testopia/contrib/drivers/java/testopia/API/Environment.java tr_query.cgi testopia/contrib/drivers/java/testopia/API/Lookup.java Bugzilla/WebService/Testopia/Build.pm template/en/default/testopia/case/text.xml.tmpl template/en/default/testopia/run/choose.html.tmpl testopia/img/triangle.png testopia/contrib/drivers/java/testopia/API/Product.java testopia/contrib/drivers/java/testopia/API/TestCaseRun.java testopia/img/validexpRed.png template/en/default/testopia/run/clone.html.tmpl testopia/contrib/drivers/java/testopia/API/TestPlan.java testopia/img/validexpYellow.png testopia/contrib/drivers/java/testopia/API/TestRun.java testopia/contrib/drivers/java/testopia/API/TestopiaTestCase.java template/en/default/testopia/caserun/filter.html.tmpl testopia/js/attachments.js Bugzilla/WebService/Testopia/Product.pm tr_insanity.pl testopia/contrib/drivers/java/testopia/API/User.java tr_caserun.cgi testopia/js/build.js testopia/contrib/drivers/java/testopia/API/lookupHelp.txt tr_run_reports.cgi testopia/contrib/drivers/java/testopia/Test/JunitToTestopia.java Bugzilla/WebService/Testopia/TestCase.pm testopia/img/BLOCKED_small.gif testopia/img/refresh.png testopia/contrib/drivers/java/testopia/Test/OptionalData.java testopia/contrib/drivers/java/testopia/Test/OptionalValues.java template/en/default/testopia/run/list.html.tmpl testopia/img/1x1.gif template/en/default/testopia/caserun/list.html.tmpl tr_show_case.cgi testopia/contrib/drivers/java/testopia/Test/TestCaseRunnable.java testopia/img/BLOCKED.gif Bugzilla/WebService/Testopia/TestCaseRun.pm testopia/contrib/drivers/java/testopia/Test/TestopiaConnector.java template/en/default/testopia/run/list.json.tmpl testopia/contrib/drivers/perl/client.pl testopia/img/BLOCKED_gray.gif template/en/default/testopia/run/navigate.html.tmpl Bugzilla/WebService/Testopia/TestPlan.pm testopia/contrib/drivers/php/.xmlrpc.inc.php tr_show_plan.cgi testopia/t/Instantiate.t testopia/contrib/drivers/php/testopia.inc.php template/en/default/testopia/run/show.html.tmpl testopia/contrib/drivers/python/testopia.py testopia/img/ERROR.gif Bugzilla/WebService/Testopia/TestRun.pm testopia/img/ERROR_small.gif testopia/img/FAILED.gif testopia/js/category.js testopia/css/print.css testopia/js/diff-tabs.js tr_show_product.cgi testopia/img/FAILED_gray.gif Bugzilla/WebService/Testopia/Testopia.pm testopia/js/environment.js tr_show_run.cgi testopia/css/xtheme-gray.css testopia/js/plan.js testopia/img/FAILED_small.gif testopia/doc/Manual.odt tr_process_run.cgi testopia/img/PAUSED_small.gif testopia/img/IDLE.gif skins/standard/testopia.css template/en/default/testopia/search/advanced.html.tmpl testopia/doc/Manual.pdf Bugzilla/Testopia/Environment.pm testopia/img/IDLE_gray.gif template/en/default/admin/params/testopia.html.tmpl tr_tags.cgi template/en/default/testopia/environment/add.html.tmpl testopia/doc/Manual.rtf template/en/default/testopia/search/case.html.tmpl template/en/default/hook/admin/products/confirm-delete.html.tmpl/confirmation/testopia.html.tmpl template/en/default/hook/bug/create/create.html.tmpl/end/tr.html.tmpl testopia/js/search.js template/en/default/testopia/environment/choose.html.tmpl tr_xmlrpc.cgi testopia/doc/Manual.xhtml testopia/img/PASSED.gif template/en/default/testopia/search/caserun.html.tmpl template/en/default/hook/bug/create/create.html.tmpl/form/tr.html.tmpl tr_list_plans.cgi template/en/default/testopia/environment/delete.html.tmpl testopia/img/PASSED_gray.gif testopia/img/tr.gif testopia/doc/Testopia.dia template/en/default/testopia/search/environment.html.tmpl template/en/default/hook/bug/create/created.html.tmpl/message/tr.html.tmpl extensions/testopia/code/enter_bug-entrydefaultvars.pl testopia/js/tags.js extensions/testopia/code/install-before_final_checks.pl template/en/default/testopia/environment/export.xml.tmpl testopia/img/PASSED_small.gif template/en/default/testopia/search/plan.html.tmpl extensions/testopia/code/install-requirements.pl template/en/default/hook/bug/edit.html.tmpl/after_custom_fields/tr.html.tmpl template/en/default/testopia/environment/form.html.tmpl testopia/img/PAUSED.gif testopia/doc/pod/webservice/CHANGELOG template/en/default/testopia/search/report-matrix.html.tmpl template/en/default/hook/bug/process/results.html.tmpl/links/tr.html.tmpl testopia/img/PAUSED_gray.gif template/en/default/testopia/environment/import.xml.tmpl template/en/default/testopia/search/run.html.tmpl extensions/testopia/code/post_bug-after_creation.pl template/en/default/hook/global/banner.html.tmpl/version/testopia.html.tmpl template/en/default/testopia/environment/list.html.tmpl extensions/testopia/code/product-confirm_delete.pl template/en/default/testopia/search/variables.none.tmpl template/en/default/hook/global/code-error.html.tmpl/errors/testopia_errors.html.tmpl testopia/img/RUNNING.gif template/en/default/testopia/environment/list.json.tmpl template/en/default/hook/global/common-links.html.tmpl/link-row/testopia.html.tmpl template/en/default/testopia/tag/form.html.tmpl template/en/default/testopia/environment/show.html.tmpl Cherrypick from trunk 2009-01-30 23:59:13 UTC Gregary Hendricks ' Bug 466605 - User Lookup fields are missing in IE7': testopia/testopia.all.js Bugzilla/Testopia/Search.pm tr_process_plan.cgi tr_new_case.cgi extensions/testopia/code/install-update_db.pl testopia/js/run.js testopia/testopia.all.ycomp.js testopia/js/case.js template/en/default/testopia/case/show.html.tmpl testopia/js/util.js tr_quicksearch.cgi testopia/js/caserun.js extensions/testopia/code/db_schema-abstract_schema.pl Bugzilla/Testopia/TestCaseRun.pm --- .project | 17 + Bugzilla/Config/Testopia.pm | 158 + Bugzilla/Testopia/Attachment.pm | 795 ++ Bugzilla/Testopia/Build.pm | 584 + Bugzilla/Testopia/Category.pm | 517 + Bugzilla/Testopia/Classification.pm | 92 + Bugzilla/Testopia/Constants.pm | 128 + Bugzilla/Testopia/Environment.pm | 1004 ++ Bugzilla/Testopia/Environment/Category.pm | 544 + Bugzilla/Testopia/Environment/Element.pm | 573 + Bugzilla/Testopia/Environment/Property.pm | 439 + Bugzilla/Testopia/Environment/Xml.pm | 585 + Bugzilla/Testopia/Product.pm | 480 + Bugzilla/Testopia/Report.pm | 368 + Bugzilla/Testopia/Search.pm | 1841 ++++ Bugzilla/Testopia/Table.pm | 515 + Bugzilla/Testopia/TestCase.pm | 2398 +++++ Bugzilla/Testopia/TestCaseRun.pm | 1193 +++ Bugzilla/Testopia/TestPlan.pm | 1469 +++ Bugzilla/Testopia/TestRun.pm | 1513 +++ Bugzilla/Testopia/TestTag.pm | 374 + Bugzilla/Testopia/Util.pm | 228 + Bugzilla/Testopia/Xml.pm | 678 ++ Bugzilla/Testopia/XmlReferences.pm | 144 + Bugzilla/Testopia/XmlTestCase.pm | 485 + Bugzilla/WebService/Testopia/Build.pm | 279 + Bugzilla/WebService/Testopia/Environment.pm | 386 + Bugzilla/WebService/Testopia/Product.pm | 423 + Bugzilla/WebService/Testopia/TestCase.pm | 1141 ++ Bugzilla/WebService/Testopia/TestCaseRun.pm | 714 ++ Bugzilla/WebService/Testopia/TestPlan.pm | 584 + Bugzilla/WebService/Testopia/TestRun.pm | 725 ++ Bugzilla/WebService/Testopia/Testopia.pm | 78 + .../code/db_schema-abstract_schema.pl | 1100 ++ .../code/enter_bug-entrydefaultvars.pl | 8 + .../code/install-before_final_checks.pl | 28 + .../testopia/code/install-requirements.pl | 36 + extensions/testopia/code/install-update_db.pl | 559 + .../testopia/code/post_bug-after_creation.pl | 29 + .../testopia/code/product-confirm_delete.pl | 27 + skins/standard/testopia.css | 266 + .../default/admin/params/testopia.html.tmpl | 85 + .../confirmation/testopia.html.tmpl | 9 + .../create/create.html.tmpl/end/tr.html.tmpl | 5 + .../create/create.html.tmpl/form/tr.html.tmpl | 8 + .../created.html.tmpl/message/tr.html.tmpl | 5 + .../after_custom_fields/tr.html.tmpl | 16 + .../results.html.tmpl/links/tr.html.tmpl | 6 + .../version/testopia.html.tmpl | 21 + .../errors/testopia_errors.html.tmpl | 28 + .../link-row/testopia.html.tmpl | 60 + .../testopia_styles.html.tmpl | 4 + .../useful-links.html.tmpl/end/tr.html.tmpl | 36 + .../errors/tr-user-error.html.tmpl | 191 + .../hook/index.html.tmpl/links/tr.html.tmpl | 30 + .../list/list.html.tmpl/links/tr.html.tmpl | 3 + .../testopia/admin/plantypes/add.html.tmpl | 42 + .../testopia/admin/plantypes/delete.html.tmpl | 24 + .../testopia/admin/plantypes/edit.html.tmpl | 43 + .../testopia/admin/plantypes/show.html.tmpl | 42 + .../en/default/testopia/admin/show.html.tmpl | 45 + .../testopia/attachment/choose.html.tmpl | 38 + .../testopia/attachment/list.json.tmpl | 21 + template/en/default/testopia/blocks.html.tmpl | 165 + .../en/default/testopia/case/add.html.tmpl | 59 + .../en/default/testopia/case/choose.html.tmpl | 38 + .../en/default/testopia/case/filter.html.tmpl | 88 + .../default/testopia/case/history.html.tmpl | 40 + .../en/default/testopia/case/list.csv.tmpl | 33 + .../en/default/testopia/case/list.html.tmpl | 101 + .../en/default/testopia/case/list.json.tmpl | 21 + .../en/default/testopia/case/list.xml.tmpl | 29 + .../default/testopia/case/navigate.html.tmpl | 76 + .../en/default/testopia/case/new-bug.xml.tmpl | 19 + .../en/default/testopia/case/show.csv.tmpl | 32 + .../en/default/testopia/case/show.html.tmpl | 721 ++ .../en/default/testopia/case/show.print.tmpl | 97 + .../en/default/testopia/case/show.xml.tmpl | 26 + .../en/default/testopia/case/text.xml.tmpl | 14 + .../default/testopia/caserun/filter.html.tmpl | 132 + .../default/testopia/caserun/list.html.tmpl | 104 + .../default/testopia/caserun/list.json.tmpl | 21 + .../testopia/environment/add.html.tmpl | 87 + .../testopia/environment/choose.html.tmpl | 39 + .../testopia/environment/delete.html.tmpl | 61 + .../testopia/environment/export.xml.tmpl | 46 + .../testopia/environment/form.html.tmpl | 96 + .../testopia/environment/import.xml.tmpl | 58 + .../testopia/environment/list.html.tmpl | 50 + .../testopia/environment/list.json.tmpl | 21 + .../testopia/environment/show.html.tmpl | 584 + .../testopia/environment/table.html.tmpl | 55 + .../en/default/testopia/export/csv.case.tmpl | 87 + .../testopia/export/csv.caseheader.tmpl | 37 + .../en/default/testopia/export/xml.case.tmpl | 77 + .../default/testopia/export/xml.footer.tmpl | 23 + .../default/testopia/export/xml.header.tmpl | 28 + .../testopia/import/importer.html.tmpl | 39 + .../en/default/testopia/messages.html.tmpl | 50 + .../testopia/plan/access-list.html.tmpl | 148 + .../en/default/testopia/plan/add.html.tmpl | 41 + .../en/default/testopia/plan/choose.html.tmpl | 53 + .../default/testopia/plan/diff_doc.html.tmpl | 150 + .../default/testopia/plan/history.html.tmpl | 33 + .../en/default/testopia/plan/list.html.tmpl | 101 + .../en/default/testopia/plan/list.json.tmpl | 21 + .../default/testopia/plan/navigate.html.tmpl | 70 + .../en/default/testopia/plan/show.csv.tmpl | 33 + .../en/default/testopia/plan/show.html.tmpl | 457 + .../en/default/testopia/plan/show.print.tmpl | 145 + .../en/default/testopia/plan/show.xml.tmpl | 31 + .../testopia/product/dashboard.html.tmpl | 79 + .../default/testopia/product/show.html.tmpl | 391 + .../en/default/testopia/quicksearch.html.tmpl | 111 + .../testopia/reports/bug-count.html.tmpl | 45 + .../bug-severity-status-form.html.tmpl | 127 + .../testopia/reports/bug-stats-form.html.tmpl | 135 + .../testopia/reports/build-coverage.html.tmpl | 104 + .../testopia/reports/completion.html.tmpl | 113 + .../testopia/reports/completion.png.tmpl | 47 + .../testopia/reports/execution.html.tmpl | 85 + .../default/testopia/reports/header.html.tmpl | 68 + .../reports/priority-breakdown.html.tmpl | 142 + .../testopia/reports/report-bar.png.tmpl | 58 + .../testopia/reports/report-line.png.tmpl | 52 + .../testopia/reports/report-pie.png.tmpl | 34 + .../testopia/reports/report-table.html.tmpl | 161 + .../default/testopia/reports/report.csv.tmpl | 84 + .../default/testopia/reports/report.html.tmpl | 118 + .../default/testopia/reports/status.html.tmpl | 134 + .../reports/test-burnout-form.html.tmpl | 126 + .../testopia/reports/untested.html.tmpl | 38 + .../en/default/testopia/run/add.html.tmpl | 45 + .../en/default/testopia/run/choose.html.tmpl | 38 + .../en/default/testopia/run/clone.html.tmpl | 101 + .../en/default/testopia/run/list.html.tmpl | 101 + .../en/default/testopia/run/list.json.tmpl | 21 + .../default/testopia/run/navigate.html.tmpl | 72 + .../en/default/testopia/run/show.html.tmpl | 573 + .../testopia/search/advanced.html.tmpl | 45 + .../en/default/testopia/search/case.html.tmpl | 498 + .../default/testopia/search/caserun.html.tmpl | 547 + .../testopia/search/environment.html.tmpl | 410 + .../en/default/testopia/search/plan.html.tmpl | 169 + .../testopia/search/report-matrix.html.tmpl | 70 + .../en/default/testopia/search/run.html.tmpl | 223 + .../testopia/search/variables.none.tmpl | 48 + template/en/default/testopia/style.none.tmpl | 58 + .../en/default/testopia/tag/form.html.tmpl | 44 + .../en/default/testopia/tag/list.html.tmpl | 66 + .../en/default/testopia/tag/show.html.tmpl | 116 + .../en/default/testopia/tag/table.html.tmpl | 44 + template/en/default/testopia/text.png.tmpl | 37 + testopia.dtd | 92 + testopia/ChangeLog | 439 + testopia/README | 154 + testopia/contrib/cvs_update.sh | 55 + testopia/contrib/drivers/java/.classpath | 15 + testopia/contrib/drivers/java/.project | 17 + .../contrib/drivers/java/OptionalData.xml | 16 + .../contrib/drivers/java/TESTS-TestSuites.xml | 46 + testopia/contrib/drivers/java/build.xml | 68 + .../drivers/java/doc/allclasses-frame.html | 46 + .../drivers/java/doc/allclasses-noframe.html | 46 + .../drivers/java/doc/constant-values.html | 140 + .../drivers/java/doc/deprecated-list.html | 140 + .../contrib/drivers/java/doc/help-doc.html | 211 + .../drivers/java/doc/index-files/index-1.html | 139 + .../java/doc/index-files/index-10.html | 151 + .../java/doc/index-files/index-11.html | 157 + .../drivers/java/doc/index-files/index-2.html | 139 + .../drivers/java/doc/index-files/index-3.html | 139 + .../drivers/java/doc/index-files/index-4.html | 139 + .../drivers/java/doc/index-files/index-5.html | 209 + .../drivers/java/doc/index-files/index-6.html | 155 + .../drivers/java/doc/index-files/index-7.html | 139 + .../drivers/java/doc/index-files/index-8.html | 139 + .../drivers/java/doc/index-files/index-9.html | 229 + testopia/contrib/drivers/java/doc/index.html | 34 + .../drivers/java/doc/overview-tree.html | 146 + .../contrib/drivers/java/doc/package-list | 1 + .../drivers/java/doc/resources/inherit.gif | Bin 0 -> 57 bytes .../contrib/drivers/java/doc/stylesheet.css | 29 + .../drivers/java/doc/testopia/API/Build.html | 339 + .../java/doc/testopia/API/Component.html | 263 + .../java/doc/testopia/API/Environment.html | 319 + .../java/doc/testopia/API/Product.html | 304 + .../java/doc/testopia/API/TestAPI.html | 247 + .../java/doc/testopia/API/TestCaseRun.html | 453 + .../java/doc/testopia/API/TestPlan.html | 608 ++ .../java/doc/testopia/API/TestRun.html | 545 + .../doc/testopia/API/TestopiaTestCase.html | 660 ++ .../drivers/java/doc/testopia/API/User.html | 262 + .../doc/testopia/API/class-use/Build.html | 138 + .../doc/testopia/API/class-use/Component.html | 138 + .../testopia/API/class-use/Environment.html | 138 + .../doc/testopia/API/class-use/Product.html | 138 + .../doc/testopia/API/class-use/TestAPI.html | 138 + .../testopia/API/class-use/TestCaseRun.html | 138 + .../doc/testopia/API/class-use/TestPlan.html | 138 + .../doc/testopia/API/class-use/TestRun.html | 138 + .../API/class-use/TestopiaTestCase.html | 138 + .../java/doc/testopia/API/class-use/User.html | 138 + .../java/doc/testopia/API/package-frame.html | 48 + .../doc/testopia/API/package-summary.html | 184 + .../java/doc/testopia/API/package-tree.html | 144 + .../java/doc/testopia/API/package-use.html | 138 + testopia/contrib/drivers/java/jar/README | 19 + .../drivers/java/testopia/API/Build.java | 326 + .../drivers/java/testopia/API/Component.java | 170 + .../java/testopia/API/Environment.java | 368 + .../drivers/java/testopia/API/Lookup.java | 152 + .../drivers/java/testopia/API/Product.java | 235 + .../java/testopia/API/TestCaseRun.java | 399 + .../drivers/java/testopia/API/TestPlan.java | 650 ++ .../drivers/java/testopia/API/TestRun.java | 494 + .../java/testopia/API/TestopiaTestCase.java | 670 ++ .../drivers/java/testopia/API/User.java | 178 + .../drivers/java/testopia/API/lookupHelp.txt | 30 + .../java/testopia/Test/JunitToTestopia.java | 313 + .../java/testopia/Test/OptionalData.java | 78 + .../java/testopia/Test/OptionalValues.java | 169 + .../drivers/java/testopia/Test/ParseAnt.java | 141 + .../java/testopia/Test/TestCaseRunnable.java | 64 + .../java/testopia/Test/TestopiaConnector.java | 354 + .../contrib/drivers/java/testopiaData.xml | 34 + testopia/contrib/drivers/perl/client.pl | 308 + testopia/contrib/drivers/php/.xmlrpc.inc.php | 3566 +++++++ testopia/contrib/drivers/php/testopia.inc.php | 2448 +++++ testopia/contrib/drivers/python/testopia.py | 2131 ++++ testopia/css/print.css | 33 + testopia/css/xtheme-gray.css | 415 + testopia/doc/Manual.odt | Bin 0 -> 147764 bytes testopia/doc/Manual.pdf | Bin 0 -> 347342 bytes testopia/doc/Manual.rtf | 1281 +++ testopia/doc/Manual.xhtml | 194 + testopia/doc/Testopia.dia | Bin 0 -> 2875 bytes testopia/doc/pod/webservice/CHANGELOG | 63 + testopia/doc/training.odp | Bin 0 -> 304335 bytes testopia/environment.dtd | 15 + testopia/img/1x1.gif | Bin 0 -> 123 bytes testopia/img/BLOCKED.gif | Bin 0 -> 538 bytes testopia/img/BLOCKED_gray.gif | Bin 0 -> 353 bytes testopia/img/BLOCKED_small.gif | Bin 0 -> 350 bytes testopia/img/ERROR.gif | Bin 0 -> 561 bytes testopia/img/ERROR_small.gif | Bin 0 -> 376 bytes testopia/img/FAILED.gif | Bin 0 -> 794 bytes testopia/img/FAILED_gray.gif | Bin 0 -> 390 bytes testopia/img/FAILED_small.gif | Bin 0 -> 361 bytes testopia/img/IDLE.gif | Bin 0 -> 570 bytes testopia/img/IDLE_gray.gif | Bin 0 -> 409 bytes testopia/img/IDLE_small.gif | Bin 0 -> 371 bytes testopia/img/PASSED.gif | Bin 0 -> 591 bytes testopia/img/PASSED_gray.gif | Bin 0 -> 394 bytes testopia/img/PASSED_small.gif | Bin 0 -> 376 bytes testopia/img/PAUSED.gif | Bin 0 -> 750 bytes testopia/img/PAUSED_gray.gif | Bin 0 -> 400 bytes testopia/img/PAUSED_small.gif | Bin 0 -> 370 bytes testopia/img/RUNNING.gif | Bin 0 -> 804 bytes testopia/img/RUNNING_gray.gif | Bin 0 -> 541 bytes testopia/img/RUNNING_small.gif | Bin 0 -> 376 bytes testopia/img/Testopia_pocket.eps | 59 + testopia/img/add.png | Bin 0 -> 339 bytes testopia/img/add.svg | 71 + testopia/img/archived.png | Bin 0 -> 2698 bytes testopia/img/archived.svg | 79 + testopia/img/aw.gif | Bin 0 -> 771 bytes testopia/img/awg.gif | Bin 0 -> 377 bytes testopia/img/cancel.gif | Bin 0 -> 375 bytes testopia/img/category.gif | Bin 0 -> 936 bytes testopia/img/circle.gif | Bin 0 -> 342 bytes testopia/img/circle.png | Bin 0 -> 29717 bytes testopia/img/city.eps | 64 + testopia/img/confirm.png | Bin 0 -> 353 bytes testopia/img/confirm.svg | 69 + testopia/img/copy.gif | Bin 0 -> 210 bytes testopia/img/copy.png | Bin 0 -> 471 bytes testopia/img/copy.svg | 148 + testopia/img/delete.png | Bin 0 -> 653 bytes testopia/img/delete.svg | 71 + testopia/img/edit.png | Bin 0 -> 761 bytes testopia/img/edit.svg | 144 + testopia/img/element.gif | Bin 0 -> 988 bytes testopia/img/env_lookup.png | Bin 0 -> 598 bytes testopia/img/env_lookup.svg | 86 + testopia/img/folder.gif | Bin 0 -> 165 bytes testopia/img/folder_blue.gif | Bin 0 -> 163 bytes testopia/img/folder_red.gif | Bin 0 -> 163 bytes testopia/img/go.png | Bin 0 -> 729 bytes testopia/img/go.svg | 81 + testopia/img/green_bar.gif | Bin 0 -> 119 bytes testopia/img/link.png | Bin 0 -> 881 bytes testopia/img/link.svg | 102 + testopia/img/lk.gif | Bin 0 -> 580 bytes testopia/img/new.png | Bin 0 -> 593 bytes testopia/img/new.svg | 131 + testopia/img/orange_bar.gif | Bin 0 -> 119 bytes testopia/img/pb.gif | Bin 0 -> 93 bytes testopia/img/pg.gif | Bin 0 -> 93 bytes testopia/img/product.gif | Bin 0 -> 925 bytes testopia/img/property.gif | Bin 0 -> 931 bytes testopia/img/red_bar.gif | Bin 0 -> 119 bytes testopia/img/refresh.png | Bin 0 -> 610 bytes testopia/img/refresh.svg | 91 + testopia/img/save.png | Bin 0 -> 468 bytes testopia/img/save.svg | 96 + testopia/img/saw.gif | Bin 0 -> 570 bytes testopia/img/selected_value.png | Bin 0 -> 1104 bytes testopia/img/square.gif | Bin 0 -> 301 bytes testopia/img/square.png | Bin 0 -> 27882 bytes testopia/img/stop.png | Bin 0 -> 760 bytes testopia/img/stop.svg | 86 + testopia/img/td.gif | Bin 0 -> 58 bytes testopia/img/testopia_big_picture.png | Bin 0 -> 44411 bytes testopia/img/testopia_city_128.png | Bin 0 -> 10376 bytes testopia/img/testopia_city_256.png | Bin 0 -> 22869 bytes testopia/img/testopia_city_512.png | Bin 0 -> 48233 bytes testopia/img/testopia_logo_128.png | Bin 0 -> 4436 bytes testopia/img/testopia_logo_256.png | Bin 0 -> 9598 bytes testopia/img/tr.gif | Bin 0 -> 61 bytes testopia/img/triangle.gif | Bin 0 -> 315 bytes testopia/img/triangle.png | Bin 0 -> 29306 bytes testopia/img/validexpRed.png | Bin 0 -> 572 bytes testopia/img/validexpYellow.png | Bin 0 -> 555 bytes testopia/import_example.csv | 20 + testopia/js/attachments.js | 353 + testopia/js/build.js | 228 + testopia/js/case.js | 1333 +++ testopia/js/caserun.js | 1845 ++++ testopia/js/category.js | 224 + testopia/js/diff-tabs.js | 123 + testopia/js/environment.js | 319 + testopia/js/plan.js | 887 ++ testopia/js/run.js | 1372 +++ testopia/js/search.js | 797 ++ testopia/js/strings.js | 28 + testopia/js/tags.js | 309 + testopia/js/util.js | 1712 +++ testopia/patch-3.2 | 305 + testopia/run_unit_tests.pl | 33 + testopia/t/API_Build.pm | 222 + testopia/t/API_Environment.pm | 235 + testopia/t/API_Product.pm | 333 + testopia/t/API_Suite.pm | 31 + testopia/t/API_TestCase.pm | 695 ++ testopia/t/API_TestCaseRun.pm | 386 + testopia/t/API_TestPlan.pm | 395 + testopia/t/API_TestRun.pm | 424 + testopia/t/Instantiate.t | 168 + testopia/t/OBJ_Attachment.pm | 339 + testopia/t/OBJ_Builds.pm | 160 + testopia/t/OBJ_Builds_GREGG.pm | 179 + testopia/t/OBJ_Category.pm | 170 + testopia/t/OBJ_Environment.pm | 144 + testopia/t/OBJ_TestCase.pm | 520 + testopia/t/SE_attachments.t | 191 + testopia/t/SE_builds.t | 189 + testopia/t/SE_caseruns.t | 289 + testopia/t/SE_cases.t | 933 ++ testopia/t/SE_categories.t | 194 + testopia/t/SE_environments.t | 174 + testopia/t/SE_plans.t | 830 ++ testopia/t/SE_query.t | 73 + testopia/t/SE_runs.t | 646 ++ testopia/t/SE_tags.t | 0 testopia/t/Test.pm | 95 + testopia/t/Testopia/Test/API/Constants.pm | 0 testopia/t/Testopia/Test/API/Util.pm | 114 + testopia/t/Testopia/Test/Constants.pm | 48 + .../t/Testopia/Test/Selenium/Constants.pm | 199 + testopia/t/Testopia/Test/Selenium/Util.pm | 179 + testopia/t/Testopia/Test/Util.pm | 55 + testopia/t/data/dummy_load.sql | 2373 ++++ testopia/testopia.all.js | 9507 +++++++++++++++++ testopia/testopia.all.ycomp.js | 1 + testopia/testrunner.pl | 79 + testopia/tools/readme | 3 + tr_admin.cgi | 104 + tr_attachment.cgi | 214 + tr_builds.cgi | 165 + tr_case_reports.cgi | 133 + tr_caserun.cgi | 202 + tr_caserun_reports.cgi | 100 + tr_categories.cgi | 103 + tr_csv2xml.pl | 701 ++ tr_draw.cgi | 38 + tr_environments.cgi | 671 ++ tr_export_environment.cgi | 67 + tr_history.cgi | 98 + tr_import_environment.cgi | 346 + tr_importer.cgi | 239 + tr_importxml.pl | 167 + tr_insanity.pl | 56 + tr_list_caseruns.cgi | 148 + tr_list_cases.cgi | 336 + tr_list_environments.cgi | 64 + tr_list_plans.cgi | 88 + tr_list_runs.cgi | 201 + tr_new_case.cgi | 206 + tr_new_environment.cgi | 88 + tr_new_plan.cgi | 128 + tr_new_run.cgi | 143 + tr_plan_access.cgi | 137 + tr_plan_reports.cgi | 212 + tr_process_case.cgi | 230 + tr_process_plan.cgi | 244 + tr_process_run.cgi | 158 + tr_product_reports.cgi | 125 + tr_query.cgi | 419 + tr_quicksearch.cgi | 448 + tr_run_reports.cgi | 379 + tr_show_case.cgi | 75 + tr_show_plan.cgi | 81 + tr_show_product.cgi | 82 + tr_show_run.cgi | 60 + tr_tags.cgi | 232 + tr_xmlrpc.cgi | 53 + 417 files changed, 98584 insertions(+) create mode 100644 .project create mode 100644 Bugzilla/Config/Testopia.pm create mode 100644 Bugzilla/Testopia/Attachment.pm create mode 100644 Bugzilla/Testopia/Build.pm create mode 100644 Bugzilla/Testopia/Category.pm create mode 100644 Bugzilla/Testopia/Classification.pm create mode 100644 Bugzilla/Testopia/Constants.pm create mode 100644 Bugzilla/Testopia/Environment.pm create mode 100644 Bugzilla/Testopia/Environment/Category.pm create mode 100644 Bugzilla/Testopia/Environment/Element.pm create mode 100644 Bugzilla/Testopia/Environment/Property.pm create mode 100644 Bugzilla/Testopia/Environment/Xml.pm create mode 100644 Bugzilla/Testopia/Product.pm create mode 100644 Bugzilla/Testopia/Report.pm create mode 100644 Bugzilla/Testopia/Search.pm create mode 100644 Bugzilla/Testopia/Table.pm create mode 100644 Bugzilla/Testopia/TestCase.pm create mode 100644 Bugzilla/Testopia/TestCaseRun.pm create mode 100644 Bugzilla/Testopia/TestPlan.pm create mode 100644 Bugzilla/Testopia/TestRun.pm create mode 100644 Bugzilla/Testopia/TestTag.pm create mode 100644 Bugzilla/Testopia/Util.pm create mode 100644 Bugzilla/Testopia/Xml.pm create mode 100644 Bugzilla/Testopia/XmlReferences.pm create mode 100644 Bugzilla/Testopia/XmlTestCase.pm create mode 100644 Bugzilla/WebService/Testopia/Build.pm create mode 100644 Bugzilla/WebService/Testopia/Environment.pm create mode 100644 Bugzilla/WebService/Testopia/Product.pm create mode 100644 Bugzilla/WebService/Testopia/TestCase.pm create mode 100644 Bugzilla/WebService/Testopia/TestCaseRun.pm create mode 100644 Bugzilla/WebService/Testopia/TestPlan.pm create mode 100644 Bugzilla/WebService/Testopia/TestRun.pm create mode 100644 Bugzilla/WebService/Testopia/Testopia.pm create mode 100644 extensions/testopia/code/db_schema-abstract_schema.pl create mode 100644 extensions/testopia/code/enter_bug-entrydefaultvars.pl create mode 100644 extensions/testopia/code/install-before_final_checks.pl create mode 100644 extensions/testopia/code/install-requirements.pl create mode 100644 extensions/testopia/code/install-update_db.pl create mode 100644 extensions/testopia/code/post_bug-after_creation.pl create mode 100644 extensions/testopia/code/product-confirm_delete.pl create mode 100644 skins/standard/testopia.css create mode 100644 template/en/default/admin/params/testopia.html.tmpl create mode 100644 template/en/default/hook/admin/products/confirm-delete.html.tmpl/confirmation/testopia.html.tmpl create mode 100644 template/en/default/hook/bug/create/create.html.tmpl/end/tr.html.tmpl create mode 100644 template/en/default/hook/bug/create/create.html.tmpl/form/tr.html.tmpl create mode 100644 template/en/default/hook/bug/create/created.html.tmpl/message/tr.html.tmpl create mode 100644 template/en/default/hook/bug/edit.html.tmpl/after_custom_fields/tr.html.tmpl create mode 100644 template/en/default/hook/bug/process/results.html.tmpl/links/tr.html.tmpl create mode 100644 template/en/default/hook/global/banner.html.tmpl/version/testopia.html.tmpl create mode 100644 template/en/default/hook/global/code-error.html.tmpl/errors/testopia_errors.html.tmpl create mode 100644 template/en/default/hook/global/common-links.html.tmpl/link-row/testopia.html.tmpl create mode 100644 template/en/default/hook/global/header.html.tmpl/additional_header/testopia_styles.html.tmpl create mode 100644 template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl create mode 100644 template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl create mode 100644 template/en/default/hook/index.html.tmpl/links/tr.html.tmpl create mode 100644 template/en/default/hook/list/list.html.tmpl/links/tr.html.tmpl create mode 100644 template/en/default/testopia/admin/plantypes/add.html.tmpl create mode 100644 template/en/default/testopia/admin/plantypes/delete.html.tmpl create mode 100644 template/en/default/testopia/admin/plantypes/edit.html.tmpl create mode 100644 template/en/default/testopia/admin/plantypes/show.html.tmpl create mode 100644 template/en/default/testopia/admin/show.html.tmpl create mode 100644 template/en/default/testopia/attachment/choose.html.tmpl create mode 100644 template/en/default/testopia/attachment/list.json.tmpl create mode 100644 template/en/default/testopia/blocks.html.tmpl create mode 100644 template/en/default/testopia/case/add.html.tmpl create mode 100644 template/en/default/testopia/case/choose.html.tmpl create mode 100644 template/en/default/testopia/case/filter.html.tmpl create mode 100644 template/en/default/testopia/case/history.html.tmpl create mode 100644 template/en/default/testopia/case/list.csv.tmpl create mode 100644 template/en/default/testopia/case/list.html.tmpl create mode 100644 template/en/default/testopia/case/list.json.tmpl create mode 100644 template/en/default/testopia/case/list.xml.tmpl create mode 100644 template/en/default/testopia/case/navigate.html.tmpl create mode 100644 template/en/default/testopia/case/new-bug.xml.tmpl create mode 100644 template/en/default/testopia/case/show.csv.tmpl create mode 100644 template/en/default/testopia/case/show.html.tmpl create mode 100644 template/en/default/testopia/case/show.print.tmpl create mode 100644 template/en/default/testopia/case/show.xml.tmpl create mode 100644 template/en/default/testopia/case/text.xml.tmpl create mode 100644 template/en/default/testopia/caserun/filter.html.tmpl create mode 100644 template/en/default/testopia/caserun/list.html.tmpl create mode 100644 template/en/default/testopia/caserun/list.json.tmpl create mode 100644 template/en/default/testopia/environment/add.html.tmpl create mode 100644 template/en/default/testopia/environment/choose.html.tmpl create mode 100644 template/en/default/testopia/environment/delete.html.tmpl create mode 100644 template/en/default/testopia/environment/export.xml.tmpl create mode 100644 template/en/default/testopia/environment/form.html.tmpl create mode 100644 template/en/default/testopia/environment/import.xml.tmpl create mode 100644 template/en/default/testopia/environment/list.html.tmpl create mode 100644 template/en/default/testopia/environment/list.json.tmpl create mode 100644 template/en/default/testopia/environment/show.html.tmpl create mode 100644 template/en/default/testopia/environment/table.html.tmpl create mode 100644 template/en/default/testopia/export/csv.case.tmpl create mode 100644 template/en/default/testopia/export/csv.caseheader.tmpl create mode 100644 template/en/default/testopia/export/xml.case.tmpl create mode 100644 template/en/default/testopia/export/xml.footer.tmpl create mode 100644 template/en/default/testopia/export/xml.header.tmpl create mode 100644 template/en/default/testopia/import/importer.html.tmpl create mode 100644 template/en/default/testopia/messages.html.tmpl create mode 100644 template/en/default/testopia/plan/access-list.html.tmpl create mode 100644 template/en/default/testopia/plan/add.html.tmpl create mode 100644 template/en/default/testopia/plan/choose.html.tmpl create mode 100644 template/en/default/testopia/plan/diff_doc.html.tmpl create mode 100644 template/en/default/testopia/plan/history.html.tmpl create mode 100644 template/en/default/testopia/plan/list.html.tmpl create mode 100644 template/en/default/testopia/plan/list.json.tmpl create mode 100644 template/en/default/testopia/plan/navigate.html.tmpl create mode 100644 template/en/default/testopia/plan/show.csv.tmpl create mode 100644 template/en/default/testopia/plan/show.html.tmpl create mode 100644 template/en/default/testopia/plan/show.print.tmpl create mode 100644 template/en/default/testopia/plan/show.xml.tmpl create mode 100644 template/en/default/testopia/product/dashboard.html.tmpl create mode 100644 template/en/default/testopia/product/show.html.tmpl create mode 100644 template/en/default/testopia/quicksearch.html.tmpl create mode 100644 template/en/default/testopia/reports/bug-count.html.tmpl create mode 100644 template/en/default/testopia/reports/bug-severity-status-form.html.tmpl create mode 100644 template/en/default/testopia/reports/bug-stats-form.html.tmpl create mode 100644 template/en/default/testopia/reports/build-coverage.html.tmpl create mode 100644 template/en/default/testopia/reports/completion.html.tmpl create mode 100644 template/en/default/testopia/reports/completion.png.tmpl create mode 100644 template/en/default/testopia/reports/execution.html.tmpl create mode 100644 template/en/default/testopia/reports/header.html.tmpl create mode 100644 template/en/default/testopia/reports/priority-breakdown.html.tmpl create mode 100644 template/en/default/testopia/reports/report-bar.png.tmpl create mode 100644 template/en/default/testopia/reports/report-line.png.tmpl create mode 100644 template/en/default/testopia/reports/report-pie.png.tmpl create mode 100644 template/en/default/testopia/reports/report-table.html.tmpl create mode 100644 template/en/default/testopia/reports/report.csv.tmpl create mode 100644 template/en/default/testopia/reports/report.html.tmpl create mode 100644 template/en/default/testopia/reports/status.html.tmpl create mode 100644 template/en/default/testopia/reports/test-burnout-form.html.tmpl create mode 100644 template/en/default/testopia/reports/untested.html.tmpl create mode 100644 template/en/default/testopia/run/add.html.tmpl create mode 100644 template/en/default/testopia/run/choose.html.tmpl create mode 100644 template/en/default/testopia/run/clone.html.tmpl create mode 100644 template/en/default/testopia/run/list.html.tmpl create mode 100644 template/en/default/testopia/run/list.json.tmpl create mode 100644 template/en/default/testopia/run/navigate.html.tmpl create mode 100644 template/en/default/testopia/run/show.html.tmpl create mode 100644 template/en/default/testopia/search/advanced.html.tmpl create mode 100644 template/en/default/testopia/search/case.html.tmpl create mode 100644 template/en/default/testopia/search/caserun.html.tmpl create mode 100644 template/en/default/testopia/search/environment.html.tmpl create mode 100644 template/en/default/testopia/search/plan.html.tmpl create mode 100644 template/en/default/testopia/search/report-matrix.html.tmpl create mode 100644 template/en/default/testopia/search/run.html.tmpl create mode 100644 template/en/default/testopia/search/variables.none.tmpl create mode 100644 template/en/default/testopia/style.none.tmpl create mode 100644 template/en/default/testopia/tag/form.html.tmpl create mode 100644 template/en/default/testopia/tag/list.html.tmpl create mode 100644 template/en/default/testopia/tag/show.html.tmpl create mode 100644 template/en/default/testopia/tag/table.html.tmpl create mode 100644 template/en/default/testopia/text.png.tmpl create mode 100644 testopia.dtd create mode 100644 testopia/ChangeLog create mode 100644 testopia/README create mode 100755 testopia/contrib/cvs_update.sh create mode 100644 testopia/contrib/drivers/java/.classpath create mode 100644 testopia/contrib/drivers/java/.project create mode 100644 testopia/contrib/drivers/java/OptionalData.xml create mode 100644 testopia/contrib/drivers/java/TESTS-TestSuites.xml create mode 100644 testopia/contrib/drivers/java/build.xml create mode 100644 testopia/contrib/drivers/java/doc/allclasses-frame.html create mode 100644 testopia/contrib/drivers/java/doc/allclasses-noframe.html create mode 100644 testopia/contrib/drivers/java/doc/constant-values.html create mode 100644 testopia/contrib/drivers/java/doc/deprecated-list.html create mode 100644 testopia/contrib/drivers/java/doc/help-doc.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-1.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-10.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-11.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-2.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-3.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-4.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-5.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-6.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-7.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-8.html create mode 100644 testopia/contrib/drivers/java/doc/index-files/index-9.html create mode 100644 testopia/contrib/drivers/java/doc/index.html create mode 100644 testopia/contrib/drivers/java/doc/overview-tree.html create mode 100644 testopia/contrib/drivers/java/doc/package-list create mode 100644 testopia/contrib/drivers/java/doc/resources/inherit.gif create mode 100644 testopia/contrib/drivers/java/doc/stylesheet.css create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/Build.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/Component.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/Environment.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/Product.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/TestAPI.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/TestCaseRun.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/TestPlan.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/TestRun.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/TestopiaTestCase.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/User.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/Build.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/Component.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/Environment.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/Product.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/TestAPI.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/TestCaseRun.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/TestPlan.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/TestRun.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/TestopiaTestCase.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/class-use/User.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/package-frame.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/package-summary.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/package-tree.html create mode 100644 testopia/contrib/drivers/java/doc/testopia/API/package-use.html create mode 100644 testopia/contrib/drivers/java/jar/README create mode 100644 testopia/contrib/drivers/java/testopia/API/Build.java create mode 100644 testopia/contrib/drivers/java/testopia/API/Component.java create mode 100644 testopia/contrib/drivers/java/testopia/API/Environment.java create mode 100644 testopia/contrib/drivers/java/testopia/API/Lookup.java create mode 100644 testopia/contrib/drivers/java/testopia/API/Product.java create mode 100644 testopia/contrib/drivers/java/testopia/API/TestCaseRun.java create mode 100644 testopia/contrib/drivers/java/testopia/API/TestPlan.java create mode 100644 testopia/contrib/drivers/java/testopia/API/TestRun.java create mode 100644 testopia/contrib/drivers/java/testopia/API/TestopiaTestCase.java create mode 100644 testopia/contrib/drivers/java/testopia/API/User.java create mode 100644 testopia/contrib/drivers/java/testopia/API/lookupHelp.txt create mode 100644 testopia/contrib/drivers/java/testopia/Test/JunitToTestopia.java create mode 100644 testopia/contrib/drivers/java/testopia/Test/OptionalData.java create mode 100644 testopia/contrib/drivers/java/testopia/Test/OptionalValues.java create mode 100644 testopia/contrib/drivers/java/testopia/Test/ParseAnt.java create mode 100644 testopia/contrib/drivers/java/testopia/Test/TestCaseRunnable.java create mode 100644 testopia/contrib/drivers/java/testopia/Test/TestopiaConnector.java create mode 100644 testopia/contrib/drivers/java/testopiaData.xml create mode 100644 testopia/contrib/drivers/perl/client.pl create mode 100644 testopia/contrib/drivers/php/.xmlrpc.inc.php create mode 100644 testopia/contrib/drivers/php/testopia.inc.php create mode 100644 testopia/contrib/drivers/python/testopia.py create mode 100644 testopia/css/print.css create mode 100644 testopia/css/xtheme-gray.css create mode 100644 testopia/doc/Manual.odt create mode 100644 testopia/doc/Manual.pdf create mode 100644 testopia/doc/Manual.rtf create mode 100644 testopia/doc/Manual.xhtml create mode 100644 testopia/doc/Testopia.dia create mode 100644 testopia/doc/pod/webservice/CHANGELOG create mode 100644 testopia/doc/training.odp create mode 100644 testopia/environment.dtd create mode 100644 testopia/img/1x1.gif create mode 100644 testopia/img/BLOCKED.gif create mode 100644 testopia/img/BLOCKED_gray.gif create mode 100644 testopia/img/BLOCKED_small.gif create mode 100644 testopia/img/ERROR.gif create mode 100644 testopia/img/ERROR_small.gif create mode 100755 testopia/img/FAILED.gif create mode 100755 testopia/img/FAILED_gray.gif create mode 100755 testopia/img/FAILED_small.gif create mode 100755 testopia/img/IDLE.gif create mode 100755 testopia/img/IDLE_gray.gif create mode 100755 testopia/img/IDLE_small.gif create mode 100755 testopia/img/PASSED.gif create mode 100755 testopia/img/PASSED_gray.gif create mode 100755 testopia/img/PASSED_small.gif create mode 100644 testopia/img/PAUSED.gif create mode 100644 testopia/img/PAUSED_gray.gif create mode 100644 testopia/img/PAUSED_small.gif create mode 100644 testopia/img/RUNNING.gif create mode 100644 testopia/img/RUNNING_gray.gif create mode 100644 testopia/img/RUNNING_small.gif create mode 100644 testopia/img/Testopia_pocket.eps create mode 100644 testopia/img/add.png create mode 100644 testopia/img/add.svg create mode 100644 testopia/img/archived.png create mode 100644 testopia/img/archived.svg create mode 100755 testopia/img/aw.gif create mode 100755 testopia/img/awg.gif create mode 100644 testopia/img/cancel.gif create mode 100644 testopia/img/category.gif create mode 100644 testopia/img/circle.gif create mode 100644 testopia/img/circle.png create mode 100644 testopia/img/city.eps create mode 100644 testopia/img/confirm.png create mode 100644 testopia/img/confirm.svg create mode 100644 testopia/img/copy.gif create mode 100644 testopia/img/copy.png create mode 100644 testopia/img/copy.svg create mode 100644 testopia/img/delete.png create mode 100644 testopia/img/delete.svg create mode 100644 testopia/img/edit.png create mode 100644 testopia/img/edit.svg create mode 100644 testopia/img/element.gif create mode 100644 testopia/img/env_lookup.png create mode 100644 testopia/img/env_lookup.svg create mode 100644 testopia/img/folder.gif create mode 100644 testopia/img/folder_blue.gif create mode 100644 testopia/img/folder_red.gif create mode 100644 testopia/img/go.png create mode 100644 testopia/img/go.svg create mode 100644 testopia/img/green_bar.gif create mode 100644 testopia/img/link.png create mode 100644 testopia/img/link.svg create mode 100755 testopia/img/lk.gif create mode 100644 testopia/img/new.png create mode 100644 testopia/img/new.svg create mode 100644 testopia/img/orange_bar.gif create mode 100755 testopia/img/pb.gif create mode 100755 testopia/img/pg.gif create mode 100644 testopia/img/product.gif create mode 100644 testopia/img/property.gif create mode 100644 testopia/img/red_bar.gif create mode 100644 testopia/img/refresh.png create mode 100644 testopia/img/refresh.svg create mode 100644 testopia/img/save.png create mode 100644 testopia/img/save.svg create mode 100755 testopia/img/saw.gif create mode 100644 testopia/img/selected_value.png create mode 100644 testopia/img/square.gif create mode 100644 testopia/img/square.png create mode 100644 testopia/img/stop.png create mode 100644 testopia/img/stop.svg create mode 100755 testopia/img/td.gif create mode 100644 testopia/img/testopia_big_picture.png create mode 100644 testopia/img/testopia_city_128.png create mode 100644 testopia/img/testopia_city_256.png create mode 100644 testopia/img/testopia_city_512.png create mode 100644 testopia/img/testopia_logo_128.png create mode 100644 testopia/img/testopia_logo_256.png create mode 100755 testopia/img/tr.gif create mode 100644 testopia/img/triangle.gif create mode 100644 testopia/img/triangle.png create mode 100644 testopia/img/validexpRed.png create mode 100644 testopia/img/validexpYellow.png create mode 100644 testopia/import_example.csv create mode 100644 testopia/js/attachments.js create mode 100644 testopia/js/build.js create mode 100644 testopia/js/case.js create mode 100755 testopia/js/caserun.js create mode 100644 testopia/js/category.js create mode 100644 testopia/js/diff-tabs.js create mode 100644 testopia/js/environment.js create mode 100644 testopia/js/plan.js create mode 100644 testopia/js/run.js create mode 100644 testopia/js/search.js create mode 100644 testopia/js/strings.js create mode 100644 testopia/js/tags.js create mode 100755 testopia/js/util.js create mode 100644 testopia/patch-3.2 create mode 100644 testopia/run_unit_tests.pl create mode 100644 testopia/t/API_Build.pm create mode 100644 testopia/t/API_Environment.pm create mode 100644 testopia/t/API_Product.pm create mode 100644 testopia/t/API_Suite.pm create mode 100644 testopia/t/API_TestCase.pm create mode 100644 testopia/t/API_TestCaseRun.pm create mode 100644 testopia/t/API_TestPlan.pm create mode 100644 testopia/t/API_TestRun.pm create mode 100644 testopia/t/Instantiate.t create mode 100755 testopia/t/OBJ_Attachment.pm create mode 100755 testopia/t/OBJ_Builds.pm create mode 100755 testopia/t/OBJ_Builds_GREGG.pm create mode 100755 testopia/t/OBJ_Category.pm create mode 100644 testopia/t/OBJ_Environment.pm create mode 100755 testopia/t/OBJ_TestCase.pm create mode 100644 testopia/t/SE_attachments.t create mode 100644 testopia/t/SE_builds.t create mode 100644 testopia/t/SE_caseruns.t create mode 100644 testopia/t/SE_cases.t create mode 100644 testopia/t/SE_categories.t create mode 100644 testopia/t/SE_environments.t create mode 100644 testopia/t/SE_plans.t create mode 100644 testopia/t/SE_query.t create mode 100644 testopia/t/SE_runs.t create mode 100644 testopia/t/SE_tags.t create mode 100755 testopia/t/Test.pm create mode 100644 testopia/t/Testopia/Test/API/Constants.pm create mode 100644 testopia/t/Testopia/Test/API/Util.pm create mode 100644 testopia/t/Testopia/Test/Constants.pm create mode 100644 testopia/t/Testopia/Test/Selenium/Constants.pm create mode 100644 testopia/t/Testopia/Test/Selenium/Util.pm create mode 100644 testopia/t/Testopia/Test/Util.pm create mode 100644 testopia/t/data/dummy_load.sql create mode 100644 testopia/testopia.all.js create mode 100644 testopia/testopia.all.ycomp.js create mode 100644 testopia/testrunner.pl create mode 100644 testopia/tools/readme create mode 100755 tr_admin.cgi create mode 100755 tr_attachment.cgi create mode 100755 tr_builds.cgi create mode 100755 tr_case_reports.cgi create mode 100755 tr_caserun.cgi create mode 100644 tr_caserun_reports.cgi create mode 100755 tr_categories.cgi create mode 100644 tr_csv2xml.pl create mode 100644 tr_draw.cgi create mode 100755 tr_environments.cgi create mode 100755 tr_export_environment.cgi create mode 100755 tr_history.cgi create mode 100755 tr_import_environment.cgi create mode 100644 tr_importer.cgi create mode 100644 tr_importxml.pl create mode 100644 tr_insanity.pl create mode 100755 tr_list_caseruns.cgi create mode 100755 tr_list_cases.cgi create mode 100755 tr_list_environments.cgi create mode 100755 tr_list_plans.cgi create mode 100755 tr_list_runs.cgi create mode 100755 tr_new_case.cgi create mode 100755 tr_new_environment.cgi create mode 100755 tr_new_plan.cgi create mode 100755 tr_new_run.cgi create mode 100644 tr_plan_access.cgi create mode 100755 tr_plan_reports.cgi create mode 100755 tr_process_case.cgi create mode 100755 tr_process_plan.cgi create mode 100755 tr_process_run.cgi create mode 100755 tr_product_reports.cgi create mode 100755 tr_query.cgi create mode 100755 tr_quicksearch.cgi create mode 100644 tr_run_reports.cgi create mode 100755 tr_show_case.cgi create mode 100755 tr_show_plan.cgi create mode 100755 tr_show_product.cgi create mode 100755 tr_show_run.cgi create mode 100755 tr_tags.cgi create mode 100755 tr_xmlrpc.cgi diff --git a/.project b/.project new file mode 100644 index 0000000..0b5ffe8 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + testopia-1.0-2.22 + + + + + + org.epic.perleditor.perlbuilder + + + + + + org.epic.perleditor.perlnature + + diff --git a/Bugzilla/Config/Testopia.pm b/Bugzilla/Config/Testopia.pm new file mode 100644 index 0000000..51446cc --- /dev/null +++ b/Bugzilla/Config/Testopia.pm @@ -0,0 +1,158 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Maciej Maczynski +# Ed Fuentetaja +# Greg Hendricks + +package Bugzilla::Config::Testopia; + +use strict; + +use Bugzilla::Config::Common; + +$Bugzilla::Config::Admin::sortkey = "20"; + +sub get_param_list { + my $class = shift; + my @param_list = ( +# { +# name => 'private-cases-log', +# type => 'b', +# default => 0, +# }, + + { + name => 'allow-test-deletion', + type => 'b', + default => 0, + }, + + { + name => 'testopia-allow-group-member-deletes', + type => 'b', + default => 0, + }, + + { + name => 'testopia-default-plan-testers-regexp', + type => 't', + }, + +# { +# name => 'print-tag-in-case-log', +# type => 'b', +# default => 0, +# }, + + { + name => 'new-case-action-template', + type => 'l', + default => qq{
    +
  1.  
  2. +
}, + }, + + { + name => 'new-case-results-template', + type => 'l', + default => qq{
    +
  1.  
  2. +
}, + }, + + { + name => 'bug-to-test-case-summary', + type => 'l', + default => 'Test for bug %id% - %summary%', + }, + + { + name => 'bug-to-test-case-action', + type => 'l', + default => 'Verify that bug %id% is fixed: %description%' + }, + + { + name => 'bug-to-test-case-results', + type => 'l', + default => '', + }, + + { + name => 'default-test-case-status', + type => 's', + choices => ['PROPOSED', 'CONFIRMED', 'DISABLED'], + default => 'PROPOSED' + }, + + { + name => 'testopia-max-allowed-plan-testers', + type => 't', + default => '500', + }, + + { + name => 'testopia-debug', + type => 's', + choices => ['ON', 'OFF', 'Developer'], + default => 'OFF' + }, +# { +# name => 'new-testrun-email-notif', +# type => 'l', +# default => 'From: bugzilla-daemon'."\n". +# 'To: %to%'."\n". +# 'Subject: Test run started.'."\n". +# "\n". +# 'Test run \'%summary%\' for product \'%product%\' and test plan \'%plan%\' has '. +# 'just been started.' +# }, + +# { +# name => 'case-failed-email-notif', +# type => 'l', +# default => 'From: bugzilla-daemon'."\n". +# 'To: %manager%'."\n". +# 'Subject: Case log \'%id%\' marked as failed.'."\n". +# "\n". +# 'Test case log \'%id%\' in test run \'%test_run%\' was marked as \'failed\' by %tester%.' +# }, + +# { +# name => 'tester-completed-email-notif', +# type => 'l', +# default => 'From: bugzilla-daemon'."\n". +# 'To: %manager%'."\n". +# 'Subject: Test run completed for tester.'."\n". +# "\n". +# 'Tester %tester% has completed the test run \'%test_run%\'.' +# }, + +# { +# name => 'test-run-completed-email-notif', +# type => 'l', +# default => 'From: bugzilla-daemon'."\n". +# 'To: %manager%'."\n". +# 'Subject: Test run completed.'."\n". +# "\n". +# 'Test run \'%test_run%\' completed.' +# } +); +} +1; diff --git a/Bugzilla/Testopia/Attachment.pm b/Bugzilla/Testopia/Attachment.pm new file mode 100644 index 0000000..889d68a --- /dev/null +++ b/Bugzilla/Testopia/Attachment.pm @@ -0,0 +1,795 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Large portions lifted uncerimoniously from Bugzilla::Attachment.pm +# and bugzilla's attachment.cgi +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::Attachment; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Error; + +use Bugzilla::Testopia::Util; + +use base qw(Exporter Bugzilla::Object); + +############################### +#### Initialization #### +############################### +use constant DB_TABLE => "test_attachments"; +use constant NAME_FIELD => "description"; +use constant ID_FIELD => "attachment_id"; +use constant DB_COLUMNS => qw( + attachment_id + submitter_id + description + filename + creation_ts + mime_type +); + +use constant REQUIRED_CREATE_FIELDS => qw(submitter_id description filename mime_type); +use constant UPDATE_COLUMNS => qw(description filename mime_type); + +use constant VALIDATORS => { + plan_id => \&_check_plan, + case_id => \&_check_case, + filename => \&_check_filename, +}; + +############################### +#### Validators #### +############################### +sub _validate_data { + my $data = shift; + my $maxsize = Bugzilla->params->{"maxattachmentsize"}; + $maxsize *= 1024; # Convert from K + + # Make sure the attachment does not exceed the maximum permitted size + my $len = $data ? length($data) : 0; + if ($maxsize && $len > $maxsize) { + my $vars = { filesize => sprintf("%.0f", $len/1024) }; + ThrowUserError("file_too_large", $vars); + } + trick_taint($data); + return $data; +} + +sub _check_plan { + my ($invocant, $plan_id) = @_; + Bugzilla::Testopia::Util::validate_test_id($plan_id, 'plan'); + trick_taint($plan_id); + return $plan_id; +} + +sub _check_case { + my ($invocant, $case_id) = @_; + Bugzilla::Testopia::Util::validate_test_id($case_id, 'case'); + trick_taint($case_id); + return $case_id; +} + +sub _check_filename { + my ($invocant, $filename) = @_; + # Remove path info (if any) from the file name. The browser should do this + # for us, but some are buggy. This may not work on Mac file names and could + # mess up file names with slashes in them, but them's the breaks. We only + # use this as a hint to users downloading attachments anyway, so it's not + # a big deal if it munges incorrectly occasionally. + $filename =~ s/^.*[\/\\]//; + + # Truncate the filename to 100 characters, counting from the end of the string + # to make sure we keep the filename extension. + $filename = substr($filename, -100, 100); + + trick_taint($filename); + return $filename; + +} + +############################### +#### Mutators #### +############################### +sub set_description { $_[0]->set('description', $_[1]); } +sub set_mime_type { $_[0]->set('mime_type', $_[1]); } +sub set_filename { $_[0]->set('filename', $_[1]); } + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (!keys %$param || $param->{PREVALIDATED}){ + bless($param, $class); + return $param; + } + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub create { + my ($class, $params) = @_; + my $dbh = Bugzilla->dbh; + + $class->SUPER::check_required_create_fields($params); + + # This is an either/or operation. We need either a plan or a case + # to attach this to. + if (!$params->{'case_id'} && !$params->{'plan_id'}){ + ThrowCodeError("testopia-missing-attachment-key"); + } + + my $field_values = $class->run_create_validators($params); + + # Windows screenshots are usually uncompressed BMP files which + # makes for a quick way to eat up disk space. Let's compress them. + # We do this before we check the size since the uncompressed version + # could easily be greater than maxattachmentsize. + if (Bugzilla->params->{'convert_uncompressed_images'} + && $field_values->{'mime_type'} eq 'image/bmp'){ + require Image::Magick; + my $img = Image::Magick->new(magick=>'bmp'); + $img->BlobToImage($field_values->{'contents'}); + $img->set(magick=>'png'); + my $imgdata = $img->ImageToBlob(); + $field_values->{'contents'} = $imgdata; + $field_values->{'mime_type'} = 'image/png'; + } + + $field_values->{contents} = _validate_data($field_values->{contents}); + $field_values->{creation_ts} = Bugzilla::Testopia::Util::get_time_stamp(); + + my $contents = $field_values->{contents}; + my $case_id = $field_values->{case_id}; + my $plan_id = $field_values->{plan_id}; + my $caserun_id = $field_values->{caserun_id}; + + delete $field_values->{contents}; + delete $field_values->{case_id}; + delete $field_values->{plan_id}; + delete $field_values->{caserun_id}; + + my $self = $class->SUPER::insert_create_data($field_values); + + # Store the data + $dbh->do("INSERT INTO test_attachment_data (attachment_id, contents) VALUES(?,?)", + undef, $self->id, $contents); + + # Link it to the case or plan + if ($case_id){ + $dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id, case_run_id) + VALUES (?,?,?)", + undef, ($self->id, $case_id, $caserun_id)); + } + elsif ($plan_id){ + $dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id) + VALUES (?,?)", + undef, ($self->id, $plan_id)); + } + + return $self; +} + +############################### +#### Methods #### +############################### + +sub TO_JSON { + my $self = shift; + my $cgi = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + # Add the calculated fields + $obj->{'isviewable'} = $self->is_browser_safe($cgi); + $obj->{'datasize'} = $self->datasize; + $obj->{'submitter'} = $self->submitter->name if $self->submitter; + $obj->{'canedit'} = $self->canedit; + $obj->{'candelete'} = $self->candelete; + $obj->{'caserun_id'} = $self->{'caserun_id'} if $self->{'caserun_id'}; + + return $json->encode($obj); +} + +# Returns 1 if the parameter is a content-type viewable in this browser +# Note that we don't use $cgi->Accept()'s ability to check if a content-type +# matches, because this will return a value even if it's matched by the generic +# */* which most browsers add to the end of their Accept: headers. + +sub is_browser_safe { + my $self = shift; + my $cgi = shift; + my $contenttype = $self->mime_type; + + # We assume we can view all text and image types + if ($contenttype =~ /^(text|image)\//) { + return 1; + } + + # Mozilla can view XUL. Note the trailing slash on the Gecko detection to + # avoid sending XUL to Safari. + if (($contenttype =~ /^application\/vnd\.mozilla\./) && + ($cgi->user_agent() =~ /Gecko\//)) + { + return 1; + } + + # If it's not one of the above types, we check the Accept: header for any + # types mentioned explicitly. + my $accept = join(",", $cgi->Accept()); + + if ($accept =~ /^(.*,)?\Q$contenttype\E(,.*)?$/) { + return 1; + } + + return 0; +} + +sub obliterate { + my $self = shift; + return 0 unless $self->candelete; + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM test_attachment_data + WHERE attachment_id = ?", undef, $self->{'attachment_id'}); + $dbh->do("DELETE FROM test_case_attachments + WHERE attachment_id = ?", undef, $self->{'attachment_id'}); + $dbh->do("DELETE FROM test_plan_attachments + WHERE attachment_id = ?", undef, $self->{'attachment_id'}); + $dbh->do("DELETE FROM test_attachments + WHERE attachment_id = ?", undef, $self->{'attachment_id'}); + return 1; +} + +sub link_plan { + my $self = shift; + my ($plan_id) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); + my ($is) = $dbh->selectrow_array( + "SELECT 1 + FROM test_plan_attachments + WHERE attachment_id = ? + AND plan_id = ?", + undef, ($self->id, $plan_id)); + if ($is) { + $dbh->bz_commit_transaction(); + return; + } + + $dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id) + VALUES (?,?)", + undef, ($self->id, $plan_id)); + $dbh->bz_commit_transaction(); +} + +sub link_case { + my $self = shift; + my ($case_id) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); + my ($is) = $dbh->selectrow_array( + "SELECT 1 + FROM test_case_attachments + WHERE attachment_id = ? + AND case_id = ?", + undef, ($self->id, $case_id)); + if ($is) { + $dbh->bz_commit_transaction(); + return; + } + + $dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id) + VALUES (?,?)", + undef, ($self->id, $case_id)); + $dbh->bz_commit_transaction(); +} + +sub unlink_plan { + my $self = shift; + my ($plan_id) = @_; + my $dbh = Bugzilla->dbh; + my ($refcount) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_plan_attachments + WHERE attachment_id = ?", undef, $self->id); + if ($refcount > 1){ + $dbh->do("DELETE FROM test_plan_attachments + WHERE plan_id = ? AND attachment_id = ?", + undef, ($plan_id, $self->id)); + } + else { + $self->obliterate; + } +} + +sub unlink_case { + my $self = shift; + my ($case_id) = @_; + my $dbh = Bugzilla->dbh; + + my ($refcount) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_case_attachments + WHERE attachment_id = ?", undef, $self->id); + if ($refcount > 1){ + $dbh->do("DELETE FROM test_case_attachments + WHERE case_id = ? AND attachment_id = ?", + undef, ($case_id, $self->id)); + } + else { + $self->obliterate; + } +} + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + foreach my $i (@{$self->cases}){ + return 0 unless $i->canview; + } + foreach my $i (@{$self->plans}){ + return 0 unless $i->canview; + } + return 1; +} + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + foreach my $i (@{$self->cases}){ + return 0 unless $i->canedit; + } + foreach my $i (@{$self->plans}){ + return 0 unless $i->canedit; + } + return 1; +} + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group("admin"); + return 0 unless $self->canedit && Bugzilla->params->{"allow-test-deletion"}; + return 1 if Bugzilla->user->id == $self->submitter->id; + foreach my $i (@{$self->cases}){ + return 0 unless $i->canedit; + } + foreach my $i (@{$self->plans}){ + return 0 unless $i->canedit; + } + return 1; +} + +############################### +#### Accessors #### +############################### + +sub id { return $_[0]->{'attachment_id'}; } +sub submitter { return Bugzilla::User->new($_[0]->{'submitter_id'}); } +sub description { return $_[0]->{'description'}; } +sub filename { return $_[0]->{'filename'}; } +sub creation_ts { return $_[0]->{'creation_ts'}; } +sub mime_type { return $_[0]->{'mime_type'}; } + +sub contents { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'contents'} if exists $self->{'contents'}; + my ($contents) = $dbh->selectrow_array("SELECT contents + FROM test_attachment_data + WHERE attachment_id = ?", + undef, $self->{'attachment_id'}); + + $self->{'contents'} = $contents; + return $self->{'contents'}; +} + +sub datasize { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'datasize'} if exists $self->{'datasize'}; + + my ($datasize) = $dbh->selectrow_array("SELECT LENGTH(contents) + FROM test_attachment_data + WHERE attachment_id = ?", + undef, $self->{'attachment_id'}); + $self->{'datasize'} = $datasize; + return $self->{'datasize'}; +} + +sub cases { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::TestCase; + + return $self->{'cases'} if exists $self->{'cases'}; + my $caseids = $dbh->selectcol_arrayref( + "SELECT case_id FROM test_case_attachments + WHERE attachment_id = ?", + undef, $self->id); + my @cases; + foreach my $id (@{$caseids}){ + push @cases, Bugzilla::Testopia::TestCase->new($id); + } + + $self->{'cases'} = \@cases; + return $self->{'cases'}; +} + +sub plans { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::TestPlan; + + return $self->{'plans'} if exists $self->{'plans'}; + my $planids = $dbh->selectcol_arrayref( + "SELECT plan_id FROM test_plan_attachments + WHERE attachment_id = ?", + undef, $self->id); + my @plans; + foreach my $id (@{$planids}){ + push @plans, Bugzilla::Testopia::TestPlan->new($id); + } + + $self->{'plans'} = \@plans; + return $self->{'plans'}; +} + +sub type { + my $self = shift; + $self->{'type'} = 'attachment'; + return $self->{'type'}; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Attachment - Attachment object for Testopia + +=head1 EXTENDS + +Bugzilla::Object + +=head1 DESCRIPTION + +This module provides support for attachments to Test Cases, Test +Plans and Test Case Runs in Testopia. Attachments can be linked +to multiple cases or plans. If linked to a test case, there is +an optional id for the case_run in which it was linked. + +=head1 SYNOPSIS + +=head2 Creating + + $attachment = Bugzilla::Testopia::Attachment->new($attachment_id); + $attachment = Bugzilla::Testopia::Attachment->new({name => $name}); + + $new_attachment = Bugzilla::Testopia::Attachment->create({name => $name, + description => $desc + ... }); + +=head2 Updating + + $attachment->set_filename($name); + $attachment->set_description($desc); + $attachment->set_mime_type($mime_type); + + $attachment->update(); + +=head2 Accessors + + my $id = $attachment->id; + my $fname = $attachment->filename; + my $desc = $attachment->description; + my $size = $attachment->datasize; + my $contents = $attachment->contents; + my $created = $attachment->creation_ts; + my $submitter = $attachment->submitter; + +=head1 FIELDS + + Table test_attachments + +---------------+------------------+------+-----+---------------------+----------------+ + | Field | Type | Null | Key | Default | Extra | + +---------------+------------------+------+-----+---------------------+----------------+ + | attachment_id | int(10) unsigned | NO | PRI | NULL | auto_increment | + | submitter_id | mediumint(9) | NO | MUL | 0 | | + | description | mediumtext | YES | | NULL | | + | filename | mediumtext | YES | | NULL | | + | creation_ts | datetime | NO | | 0000-00-00 00:00:00 | | + | mime_type | varchar(100) | NO | | | | + +---------------+------------------+------+-----+---------------------+----------------+ + + Table test_attachment_data + +---------------+----------+------+-----+---------+-------+ + | Field | Type | Null | Key | Default | Extra | + +---------------+----------+------+-----+---------+-------+ + | attachment_id | int(11) | NO | MUL | | | + | contents | longblob | YES | | NULL | | + +---------------+----------+------+-----+---------+-------+ + +=over + +=item C + +The unique id of this attachment in the database. + +=item C + +Timestamp - when this attachment was created + +=item C + +A description of this attachment. Becomes the link on the plan and case pages. + +=item C + +The file name from the users harddrive before uploading with its path removed. + +=item C + +The MIME type associated with this attachment. This is used to determine if the +attachment if viewable in a browser. + +=item C + +The actual attachment data. This is stored in a separate table to reduce lookup times. + +=item C + +The ID of the person that uploaded the attachment. + +=back + +=head1 METHODS + +=over + +=item C + + Description: Used to load an existing attachment from the database. + + Params: $param - An integer representing the ID in the database + or a hash with the "name" key representing the named + attachment in the database. + + Returns: A blessed Bugzilla::Testopia::Attachment object + +=item C + + Description: Check that the current attachment can be safely deleted and that + the current user has rights to do so. + + Params: none. + + Returns: 0 if the user does not have rights to delete this attachment. + 1 if the user does have rights. + +=item C + + Description: Check that the current user has rights to edit this attachment. + + Params: none. + + Returns: 0 if the user does not have rights. + 1 if the user does have rights. + +=item C + + Description: Chec that the current user has rights to view this attachment. + + Params: none. + + Returns: 0 if the user does not have rights. + 1 if the user does have rights. + +=item C + + Description: Creates a new attachment object and stores it in the database. + Also links the associated plans or cases to the object. + + Params: A hash with keys and values matching the fields of the attachment to + be created. + + Returns: The newly created object. + +=item C + + Description: Checks that the attachment is viewable in the browser based on + its mime_type. + + Params: CGI - a Bugzilla::CGI object. + + Returns: 1 if this attachment can be viewed inline in the browser. + 0 if the attachment must be downloaded for viewing in an external + application. + +=item C + + Description: Links this attachment to the specified case. + + Params: case_id - id of the case to link to. + + Returns: nothing. + +=item C + + Description: Links this attachment to the specified plan. + + Params: plan_id - id of the plan to link to. + + Returns: nothing. + +=item C + + Description: Completely removes this attachment from the database and clears + references to it. + + Params: none. + + Returns: nothing. + +=item C + + Description: Replaces the current attachment's description. Must call update to + store the change in the database. + + Params: text - the new description. + + Returns: nothing. + +=item C + + Description: Sets the isactive field. + + Params: string - the new filename + + Returns: nothing. + +=item C + + Description: Changes the assigned mime_type + + Params: string - the new mime_type + + Returns: nothing. + +=item C DEPRECATED + + Description: Similar to create except validation is not performed during store. + + Params: none. + + Returns: The id of the newly stored attachment. + +=item C + + Description: Outputs a JSON representation of the object. + + Params: none + + Returns: A JSON string. + +=item C + + Description: Unlinks the this attachment from the specified test case. If only + attached to a single case, delete the attachment instead. + + Params: case_id - id of the case to unlink. + + Returns: nothing. + +=item C + + Description: Unlinks the this attachment from the specified test plan. If only + attached to a single plan, delete the attachment instead. + + Params: plan_id - id of the plan to unlink. + + Returns: nothing. + +=back + +=head1 ACCESSORS + +=over + +=item C + + Returns a list of TestCase objects that this attachment is associated with. + +=item C + + Returns the content data of this attachment. + +=item C + + Returns the timestamp when this attachment was created. + +=item C + + Returns the size in byts of the contents of this attachment. + +=item C + + Returns the description of this attachment. + +=item C + + Returns the filename from the users harddrive that was uploaded when the + attachemnt was created with any path information stripped off. + +=item C + + Returns the id of the attachment. + +=item C + + Returns the MIME type of this attachment. + +=item C + + Returns a list of TestPlan objects associated with this attachment. + +=item C + + Returns the login name of the person who submitted this attachment. + +=item C + + Returns "attachment". For use in internal code. + +=back + +=head1 SEE ALSO + +=over + +L +L +L + +=back + +=head1 AUTHOR + +Greg Hendricks diff --git a/Bugzilla/Testopia/Build.pm b/Bugzilla/Testopia/Build.pm new file mode 100644 index 0000000..be8fcd5 --- /dev/null +++ b/Bugzilla/Testopia/Build.pm @@ -0,0 +1,584 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::Build; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Testopia::Product; +use JSON; + +use base qw(Exporter Bugzilla::Object); +@Bugzilla::Testopia::Build::EXPORT = qw(check_build); + +############################### +#### Initialization #### +############################### +use constant DB_TABLE => "test_builds"; +use constant NAME_FIELD => "name"; +use constant ID_FIELD => "build_id"; +use constant DB_COLUMNS => qw( + build_id + product_id + name + description + milestone + isactive +); + +use constant REQUIRED_CREATE_FIELDS => qw(product_id name milestone isactive); +use constant UPDATE_COLUMNS => qw(name description milestone isactive); + +use constant VALIDATORS => { + product_id => \&_check_product, + isactive => \&_check_isactive, +}; + +############################### +#### Validators #### +############################### +sub _check_product { + my ($invocant, $product_id) = @_; + $product_id = trim($product_id); + + ThrowUserError("testopia-create-denied", {'object' => 'build'}) unless Bugzilla->user->in_group('Testers'); + + my $product; + if (trim($product_id) !~ /^\d+$/ ){ + $product = Bugzilla::Product::check_product($product_id); + } + else { + $product = Bugzilla::Testopia::Product->new($product_id); + } + + if (ref $invocant){ + $invocant->{'product'} = $product; + return $product->id; + } + return $product; +} + +sub _check_name { + my ($invocant, $name, $product_id) = @_; + $name = clean_text($name) if $name; + + if (!defined $name || $name eq '') { + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}); + } + + trick_taint($name); + + # Check that we don't already have a build with that name in this product. + my $orig_id = check_build($name, $product_id); + my $notunique; + + if (ref $invocant){ + # If updating, we have matched ourself at least + $notunique = 1 if (($orig_id && $orig_id != $invocant->id)) + } + else { + # In new build any match is one too many + $notunique = 1 if $orig_id; + } + + ThrowUserError('testopia-name-not-unique', + {'object' => 'Build', + 'name' => $name}) if $notunique; + + return $name; +} + +sub _check_milestone { + my ($invocant, $milestone, $product) = @_; + if (ref $invocant){ + $product = $invocant->product; + } + $milestone = trim($milestone); + $milestone = Bugzilla::Milestone->check({product => $product, name => $milestone}); + return $milestone->name; +} + +sub _check_isactive { + my ($invocant, $isactive) = @_; + ThrowCodeError('bad_arg', {argument => 'isactive', function => 'set_isactive'}) unless ($isactive =~ /(1|0)/); + return $isactive; +} + +############################### +#### Mutators #### +############################### +sub set_description { $_[0]->set('description', $_[1]); } +sub set_isactive { $_[0]->set('isactive', $_[1]); } +sub set_milestone { + my ($self, $value) = @_; + $value = $self->_check_milestone($value); + $self->set('milestone', $value); +} +sub set_name { + my ($self, $value) = @_; + + $value = $self->_check_name($value, $self->product); + $self->set('name', $value); +} + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (!keys %$param || $param->{PREVALIDATED}){ + bless($param, $class); + return $param; + } + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $product = $params->{product_id}; # Returns actual product object + + $params->{milestone} = $class->_check_milestone($params->{milestone}, $product); + $params->{name} = $class->_check_name($params->{name}, $product); + + return $params; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{isactive} = 1; + $field_values->{product_id} = $field_values->{product_id}->id; + my $self = $class->SUPER::insert_create_data($field_values); + + return $self; +} +############################### +#### Functions #### +############################### +sub check_build { + my ($name, $product, $throw) = @_; + my $dbh = Bugzilla->dbh; + my $is = $dbh->selectrow_array( + "SELECT build_id FROM test_builds + WHERE name = ? AND product_id = ?", + undef, $name, $product->id); + if ($throw){ + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $name}) unless $is; + return Bugzilla::Testopia::Build->new($is); + } + return $is; +} + +############################### +#### Methods #### +############################### +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + return $json->encode($obj); +} + +############################### +#### Accessors #### +############################### +sub id { return $_[0]->{'build_id'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub name { return $_[0]->{'name'}; } +sub description { return $_[0]->{'description'};} +sub milestone { return $_[0]->{'milestone'};} +sub isactive { return $_[0]->{'isactive'};} + +sub bugs { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'bugs'} if exists $self->{'bugs'}; + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT bug_id + FROM test_case_bugs b + JOIN test_case_runs r ON r.case_run_id = b.case_run_id + WHERE r.build_id = ?", + undef, $self->id); + my @bugs; + foreach my $id (@{$ref}){ + push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id); + } + $self->{'bugs'} = \@bugs if @bugs; + $self->{'bug_list'} = join(',', @$ref); + return $self->{'bugs'}; +} + +sub product { + my ($self) = @_; + + return $self->{'product'} if exists $self->{'product'}; + + $self->{'product'} = Bugzilla::Testopia::Product->new($self->product_id); + return $self->{'product'}; +} + +sub run_count { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'run_count'} if exists $self->{'run_count'}; + + $self->{'run_count'} = $dbh->selectrow_array( + "SELECT COUNT(run_id) FROM test_runs + WHERE build_id = ?", undef, $self->{'build_id'}); + + return $self->{'run_count'}; +} + +sub case_run_count { + my $self = shift; + my ($status_id, $builds) = @_; + my $dbh = Bugzilla->dbh; + + my @build_ids; + if ($builds){ + push @build_ids, $_->id foreach (@$builds); + } + push @build_ids, $self->id if $self->id; + + my $ids = join (',', @build_ids); + + my $query = "SELECT COUNT(case_run_id) FROM test_case_runs + WHERE build_id IN (". $ids . ")"; + $query .= " AND case_run_status_id = ?" if $status_id; + + my $count; + if ($status_id){ + $count = $dbh->selectrow_array($query, undef, ($status_id)); + } + else { + $count = $dbh->selectrow_array($query); + } + + return $count; +} + +sub runs { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'runs'} if exists $self->{'runs'}; + + require Bugzilla::Testopia::TestRun; + + my $runids = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs + WHERE build_id = ?", + undef, $self->id); + my @runs; + foreach my $id (@{$runids}){ + push @runs, Bugzilla::Testopia::TestRun->new($id); + } + + $self->{'runs'} = \@runs; + return $self->{'runs'}; +} + +=head2 caseruns + +Returns a reference to a list of test caseruns useing this build + +=cut + +sub caseruns { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'caseruns'} if exists $self->{'caseruns'}; + + require Bugzilla::Testopia::TestCaseRun; + + my $ids = $dbh->selectcol_arrayref("SELECT case_run_id FROM test_case_runs + WHERE build_id = ?", + undef, $self->id); + my @caseruns; + foreach my $id (@{$ids}){ + push @caseruns, Bugzilla::Testopia::TestCaseRun->new($id); + } + + $self->{'caseruns'} = \@caseruns; + return $self->{'caseruns'}; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Build + +=head1 EXTENDS + +Bugzilla::Object + +=head1 DESCRIPTION + +Builds are used to classify test runs. They correspond to the results of +a period of work in software development. Builds are product level attributes +and are associated with a milestone if targetmilestones are used in Bugzilla. + +=head1 SYNOPSIS + +=head2 Creating + + $build = Bugzilla::Testopia::Build->new($build_id); + $build = Bugzilla::Testopia::Build->new({name => $name}); + + $new_build = Bugzilla::Testopia::Build->create({name => $name, + description => $desc + ... }); + +=head2 Updating + + $build->set_name($name); + $build->set_description($name); + $build->set_milestone($milestone); + $build->set_isactive($isactive); + + $build->update(); + +=head2 Accessors + + my $id = $build->id; + my $name = $build->name; + my $desc = $build->description; + my $pid = $build->product_id; + my $milestone = $build->milestone; + my $crc = $build->case_run_count; + my $active = $build->isactive; + +=head1 FIELDS + + +-------------+------------------+------+-----+---------+----------------+ + | Field | Type | Null | Key | Default | Extra | + +-------------+------------------+------+-----+---------+----------------+ + | build_id | int(10) unsigned | NO | PRI | NULL | auto_increment | + | product_id | smallint(6) | NO | MUL | 0 | | + | milestone | varchar(20) | YES | MUL | NULL | | + | name | varchar(255) | YES | MUL | NULL | | + | description | text | YES | | NULL | | + | isactive | tinyint(4) | NO | | 1 | | + +-------------+------------------+------+-----+---------+----------------+ + +=over + +=item C + +The unique id of this build in the database. + +=item C B + +A unique name for this build. + +=item C B B + +The id of the Bugzilla product this build is attached to. + +=item C I + +The value from the Bugzilla product milestone table this build is associated with. +Defautlts to the product default milestone. + +=item C I + +A description of this build. + +=item C I + +Boolean - Determines whether to show this build in lists for selection. + Defaults to true. + +=back + +=head1 FUNCTIONS + +=over + +=item C + + Description: Checks if a build of a given name exists for a given product. + + Params: name - string representing the name to check for. + product_id - the product to lookup the build in. + + Returns: The id of the build if one matches. + undef if it does not match any build. + +=back + +=head1 METHODS + +=over + +=item C + + Description: Used to load an existing build from the database. + + Params: $param - An integer representing the ID in the database + or a hash with the "name" key representing the named + build in the database. + + Returns: A blessed Bugzilla::Testopia::Build object + +=item C + + Description: Creates a new build object and stores it in the database + + Params: A hash with keys and values matching the fields of the build to + be created. + + Returns: The newly created object. + +=item C + + Description: Replaces the current build's description. Must call update to + store the change in the database. + + Params: text - the new description. + + Returns: nothing. + +=item C + + Description: Sets the isactive field. + + Params: boolean - 1 for active 0 for inactive. + + Returns: nothing. + +=item C + + Description: Assigns this build to a different milestone + + Params: string - the new milestone value + + Returns: nothing. + +=item C + + Description: Renames the current build. If the new name is already in use + by another build in this product, an error will be thrown. + The update method must be called to make the change in the database. + + Params: string - the new name + + Returns: nothing. + +=item C + + Description: Outputs a JSON representation of the object. + + Params: none + + Returns: A JSON string. + +=back + +=head1 ACCESSORS + +=over + +=item C + + Params: case_run_status_id - optional; + + Returns: The number of case-runs in this build. Optionally for a given status. + +=item C + + Params: none + + Returns: A list of blessed caserun objects that use this build + +=item C + + Returns the description of this build. + +=item C + + Returns the id of the build + +=item C + + Returns 1 if this build is visible in pick lists for runs and caserund and 0 if not. + +=item C + + Returns the milestone value that this build is associated with. + +=item C + + Returns the name of this build + +=item C + + Returns a Bugzilla::Testopia::Product object of the product this build is of. + +=item C + + Returns the product id of the build. + +=item C + + Returns an integer representing the number of runs this build is associated to. + +=item C + + Params: none + + Returns: A list of blessed run objects that use this build + +=back + +=head1 SEE ALSO + +=over + +L +L +L + +=back + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/Testopia/Category.pm b/Bugzilla/Testopia/Category.pm new file mode 100644 index 0000000..a3add35 --- /dev/null +++ b/Bugzilla/Testopia/Category.pm @@ -0,0 +1,517 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Maciej Maczynski are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::Category; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Testopia::Product; + +use JSON; + +use base qw(Exporter Bugzilla::Object); +@Bugzilla::Testopia::Category::EXPORT = qw(check_case_category); + +############################### +#### Initialization #### +############################### +use constant DB_TABLE => "test_case_categories"; +use constant NAME_FIELD => "name"; +use constant ID_FIELD => "category_id"; +use constant DB_COLUMNS => qw( + category_id + product_id + name + description +); + +use constant REQUIRED_CREATE_FIELDS => qw(product_id name); +use constant UPDATE_COLUMNS => qw(name description); + +use constant VALIDATORS => { + product_id => \&_check_product, +}; + +############################### +#### Validators #### +############################### +sub _check_product { + my ($invocant, $product_id) = @_; + $product_id = trim($product_id); + + ThrowUserError("testopia-create-denied", {'object' => 'category'}) unless Bugzilla->user->in_group('Testers'); + + my $product; + if (trim($product_id) !~ /^\d+$/ ){ + $product = Bugzilla::Product::check_product($product_id); + $product = Bugzilla::Testopia::Product->new($product->id); + } + else { + $product = Bugzilla::Testopia::Product->new($product_id); + } + + + if (ref $invocant){ + $invocant->{'product'} = $product; + return $product->id; + } + return $product; +} + +sub _check_name { + my ($invocant, $name, $product) = @_; + $name = clean_text($name) if $name; + + if (!defined $name || $name eq '') { + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}); + } + + trick_taint($name); + + # Check that we don't already have a build with that name in this product. + my $orig_id = check_case_category($name, $product); + my $notunique; + + if (ref $invocant){ + # If updating, we have matched ourself at least + $notunique = 1 if (($orig_id && $orig_id != $invocant->id)) + } + else { + # In new build any match is one too many + $notunique = 1 if $orig_id; + } + + ThrowUserError('testopia-name-not-unique', + {'object' => 'Case Category', + 'name' => $name}) if $notunique; + + return $name; +} +############################## +#### Mutators #### +############################### +sub set_description { $_[0]->set('description', $_[1]); } +sub set_name { + my ($self, $value) = @_; + $value = $self->_check_name($value, $self->product); + $self->set('name', $value); +} + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (!keys %$param || $param->{PREVALIDATED}){ + bless($param, $class); + return $param; + } + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $product = $params->{product_id}; + + $params->{name} = $class->_check_name($params->{name}, $product); + + return $params; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{product_id} = $field_values->{product_id}->id; + my $self = $class->SUPER::insert_create_data($field_values); + + return $self; +} + +############################### +#### Functions #### +############################### +sub check_case_category { + my ($name, $product) = @_; + my $dbh = Bugzilla->dbh; + my $is = $dbh->selectrow_array( + "SELECT category_id FROM test_case_categories + WHERE name = ? AND product_id = ?", + undef, $name, $product->id); + return $is; +} + +############################### +#### Methods #### +############################### +sub store { + my $self = shift; + my $dbh = Bugzilla->dbh; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'category_id'} DB_COLUMNS); + + $dbh->do("INSERT INTO test_case_categories ($columns) VALUES (?,?,?)", + undef, ($self->{'product_id'}, $self->{'name'}, $self->{'description'})); + my $key = $dbh->bz_last_key( 'test_case_categories', 'category_id' ); + return $key; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + return $json->encode($obj); +} + +sub remove { + my $self = shift; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_case_categories + WHERE category_id = ?", undef, + $self->{'category_id'}); +} + +sub candelete { + my $self = shift; + return 0 unless Bugzilla->user->in_group('Testers'); + return 0 if ($self->case_count); + return 1; +} + +############################### +#### Accessors #### +############################### +sub id { return $_[0]->{'category_id'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub name { return $_[0]->{'name'}; } +sub description { return $_[0]->{'description'}; } + +sub product { + my ($self) = @_; + + return $self->{'product'} if exists $self->{'product'}; + + $self->{'product'} = Bugzilla::Testopia::Product->new($self->product_id); + return $self->{'product'}; +} + +sub case_count { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'case_count'} if exists $self->{'case_count'}; + + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(case_id) + FROM test_cases + WHERE category_id = ?", + undef, $self->{'category_id'}); + $self->{'case_count'} = $count; + return $self->{'case_count'}; +} + +sub plan_case_ids { + my ($self, $plan_id) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'case_ids'} if exists $self->{'case_ids'}; + + $self->{'case_ids'} = $dbh->selectcol_arrayref( + "SELECT DISTINCT test_cases.case_id + FROM test_cases + INNER JOIN test_case_plans ON test_case_plans.case_id = test_cases.case_id + WHERE category_id = ? AND test_case_plans.plan_id = ?", + undef, ($self->{'category_id'}, $plan_id)); + + return $self->{'case_ids'}; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Category - An object representing a test case category + +=head1 EXTENDS + +Bugzilla::Object + +=head1 DESCRIPTION + +Categories are used to classify test cases. Each test case must +belong to one category. Categories are product level attributes. +Every plan in a product will have access to that product's categories. + +=head1 SYNOPSIS + +=head2 Creating + + $category = Bugzilla::Testopia::Category->new($category_id); + $category = Bugzilla::Testopia::Category->new({name => $name}); + + $new_category = Bugzilla::Testopia::Category->create({name => $name, + description => $desc}); + +=head2 Updating + + $category->set_name($name); + $category->set_description($name); + + $category->update(); + +=head2 Accessors + + my $id = $category->id; + my $name = $category->name; + my $desc = $category->description; + my $c_cont = $category->case_count; + my $pid = $category->product_id; + my $case_ids = $category->plan_case_ids; + +=head1 FIELDS + + Table test_case_categories + +-------------+----------------------+------+-----+---------+----------------+ + | Field | Type | Null | Key | Default | Extra | + +-------------+----------------------+------+-----+---------+----------------+ + | category_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment | + | product_id | smallint(6) | NO | MUL | 0 | | + | name | varchar(240) | NO | MUL | | | + | description | mediumtext | YES | | NULL | | + +-------------+----------------------+------+-----+---------+----------------+ + +=over + +=item C + +The unique id in the database. + +=item C + +The product id of the Bugzilla product this category belongs to. + +=item C + +A unique name for this category + +=item C + +A detailed description for this category. + +=back + +=head1 FUNCTIONS + +=over + +=item C + + Description: Checks if a category of a given name exists for a given product. + + Params: name - string representing the name to check for. + product_id - the product to lookup the category in. + + Returns: The id of the category if one matches. + undef if it does not match any category. + +=back + +=head1 METHODS + +=over + +=item C + + Description: Used to load an existing Category from the database. + + Params: $param - An integer representing the Category ID in the database + or a hash with the "name" key representing the named + category in the database. + + Returns: A blessed Bugzilla::Testopia::Category object + +=back + +=over + +=item C + + Description: Tests to see if the current category can be safely deleted from + the database. To be a candidate for removal, there can be no + assigned test cases with this category. Also, the user must be in + the Testers group. + + Params: none. + + Returns: 1 if this category can be safely removed. + 0 if this category cannot be removed safely or if the logged in user + does not have sufficient rights to perform the operation. + +=back + +=over + +=item C + + Description: Creates a new category object and stores it in the database + + Params: A hash with keys and values matching the fields of the category to + be created. + + Returns: The newly created object + +=back + +=over + +=item C + + Description: Looks up the case ids assigned to this category in a given plan. + + Params: The plan id to look up. + + Returns: Integer representing the count of cases found with this category. + +=back + +=over + +=item C + + Description: Completely removes this category from the database. This should not + be called unless candelete has returned true. + + Params: none. + + Returns: nothing. + +=back + +=over + +=item C + + Description: Replaces the current category's description. Must call update to + store the change in the database. + + Params: text - the new description. + + Returns: nothing. + +=back + +=over + +=item C + + Description: Renames the current category. If the new name is already in use + by another category in this product, an error will be thrown. + The update method must be called to make the change in the database. + + Params: string - the new name + + Returns: nothing. + +=back + +=over + +=item C + + Description: Outputs a JSON representation of the object. + + Params: none + + Returns: A JSON string. + +=back + +=head1 ACCESSORS + +=over + +=item C + + Returns an integer representing the number of cases found in this category. + +=back + +=over + +=item C + + Returns the description of the category. + +=back + +=over + +=item C + + Returns the category id + +=back + +=over + +=item C + + Returns the name of the category. + +=back + +=over + +=item C + + Returns the id of the product this category belongs to + +=back + +=head1 SEE ALSO + +L + +L + +L + +=head1 AUTHOR + +Greg Hendricks diff --git a/Bugzilla/Testopia/Classification.pm b/Bugzilla/Testopia/Classification.pm new file mode 100644 index 0000000..8270cf6 --- /dev/null +++ b/Bugzilla/Testopia/Classification.pm @@ -0,0 +1,92 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Andrew Nelson + +package Bugzilla::Testopia::Classification; + +use strict; + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Config; + +# Extends Bugzilla::Classification; +use base "Bugzilla::Classification"; + +use Bugzilla; + +sub user_visible_products { + my $self = shift; + my $dbh = Bugzilla->dbh; + + if (!$self->{'products'}) { + my $query = "SELECT id FROM products " . + "LEFT JOIN group_control_map " . + "ON group_control_map.product_id = products.id "; + if (Bugzilla->params->{'useentrygroupdefault'}) { + $query .= "AND group_control_map.entry != 0 "; + } else { + $query .= "AND group_control_map.membercontrol = " . + CONTROLMAPMANDATORY . " "; + } + if (%{Bugzilla->user->groups}) { + $query .= "AND group_id NOT IN(" . + join(',', values(%{Bugzilla->user->groups})) . ") "; + } + $query .= "WHERE group_id IS NULL AND products.classification_id= ? ORDER BY products.name"; + + my $product_ids = $dbh->selectcol_arrayref($query, undef, $self->id); + + my @products; + foreach my $product_id (@$product_ids) { + push (@products, new Bugzilla::Testopia::Product($product_id)); + } + $self->{'user_visible_products'} = \@products; + } + return $self->{'user_visible_products'}; +} + +sub products_to_json { + my $self = shift; + my ($disable_move) = @_; + my $json = new JSON; + + $disable_move ||= ''; + $disable_move = ',"addChild","move"' if $disable_move; + my $products = $self->user_visible_products; + + my @values; + + foreach my $product (@$products){ + my $leaf; + + if(scalar @{$product->environment_categories}> 0){ + $leaf = JSON::false; + } + else{ + $leaf = JSON::true; + } + push @values, {text=> $product->{'name'}, id=> $product->{'id'} . ' product', type=> 'product', leaf=>$leaf, draggable => JSON::false, cls => 'product'}; + } + + return $json->encode(\@values); + +} +1; \ No newline at end of file diff --git a/Bugzilla/Testopia/Constants.pm b/Bugzilla/Testopia/Constants.pm new file mode 100644 index 0000000..6e30c95 --- /dev/null +++ b/Bugzilla/Testopia/Constants.pm @@ -0,0 +1,128 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::Constants; +use strict; +use base qw(Exporter); + +@Bugzilla::Testopia::Constants::EXPORT = qw( +PROPOSED +CONFIRMED +DISABLED + +IDLE +PASSED +FAILED +RUNNING +PAUSED +BLOCKED +ERROR + +TR_READ +TR_WRITE +TR_DELETE +TR_ADMIN + +REL_AUTHOR +REL_EDITOR +REL_TESTER +REL_TEST_CC + +TR_RELATIONSHIPS + +CASE_RUN_STATUSES + +SAVED_SEARCH +SAVED_REPORT +SAVED_FILTER +SAVED_DASHBORD + +); + +# +# Fields to include when exporting a Test Case. +# +# All _id fields but case_id are converted to a string representation. +# +@Bugzilla::Testopia::Constants::TESTCASE_EXPORT = qw( +case_id +summary +set_up +break_down +action +expected_results +alias +arguments +author_id +blocks +case_status_id +category_id +components +creation_date +default_tester_id +depends_on +isautomated +plans +priority_id +requirement +script +tags +version +); + +@Bugzilla::Constants::EXPORT_OK = qw(contenttypes); + +# Test Case Status +use constant PROPOSED => 1; +use constant CONFIRMED => 2; +use constant DISABLED => 3; + +# Test case Run Status +use constant IDLE => 1; +use constant PASSED => 2; +use constant FAILED => 3; +use constant RUNNING => 4; +use constant PAUSED => 5; +use constant BLOCKED => 6; +use constant ERROR => 7; + +use constant CASE_RUN_STATUSES => IDLE, PASSED, FAILED, RUNNING, PAUSED, BLOCKED, ERROR; + +# Test Plan Permissions (bit flags) +use constant TR_READ => 1; +use constant TR_WRITE => 2; +use constant TR_DELETE => 4; +use constant TR_ADMIN => 8; + +# Save search types +use constant SAVED_SEARCH => 0; +use constant SAVED_REPORT => 1; +use constant SAVED_FILTER => 2; +use constant SAVED_DASHBORD => 3; + +# Testopia Relationships +use constant REL_AUTHOR => 100; +use constant REL_EDITOR => 101; +use constant REL_TESTER => 102; +use constant REL_TEST_CC => 103; + +use constant RELATIONSHIPS => REL_AUTHOR, REL_EDITOR, REL_TESTER, REL_TEST_CC; + +1; diff --git a/Bugzilla/Testopia/Environment.pm b/Bugzilla/Testopia/Environment.pm new file mode 100644 index 0000000..8d715c5 --- /dev/null +++ b/Bugzilla/Testopia/Environment.pm @@ -0,0 +1,1004 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden +# Andrew Nelson + +=head1 NAME + +Bugzilla::Testopia::Environment - A test environment + +=head1 DESCRIPTION + +Environments are a set of parameters dictating what conditions +a test was conducted in. Each test run must have an environment. +Environments can be very simple or very complex. + +Environments are comprised of Elemements, Properties, and Values. +Elements can be nested within other elements. Each element can have +zero or more properties. Each property can only have one value selected +of the possible values. + +=head1 SYNOPSIS + + $env = Bugzilla::Testopia::Environment->new($env_id); + $env = Bugzilla::Testopia::Environment->new(\%env_hash); + +=cut + +package Bugzilla::Testopia::Environment; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::User; +use Bugzilla::Config; + +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Property; + +use JSON; + +use base qw(Exporter Bugzilla::Object); +@Bugzilla::Bug::EXPORT = qw(check_environment); + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + environment_id + product_id + name + isactive + +=cut +use constant DB_TABLE => "test_environments"; +use constant NAME_FIELD => "name"; +use constant ID_FIELD => "environment_id"; +use constant DB_COLUMNS => qw( + environment_id + product_id + name + isactive +); + +use constant REQUIRED_CREATE_FIELDS => qw(name product_id); +use constant UPDATE_COLUMNS => qw(name isactive); + +use constant VALIDATORS => { + product_id => \&_check_product, + isactive => \&_check_isactive, +}; + +our constant $max_depth = 7; + +############################### +#### Validators #### +############################### +sub _check_product { + my ($invocant, $product_id) = @_; + + $product_id = trim($product_id); + + require Bugzilla::Testopia::Product; + + my $product; + if (trim($product_id) !~ /^\d+$/ ){ + $product = Bugzilla::Product::check_product($product_id); + $product = Bugzilla::Testopia::Product->new($product->id); + } + else { + $product = Bugzilla::Testopia::Product->new($product_id); + } + + ThrowUserError("testopia-create-denied", {'object' => 'environment'}) unless $product->canedit; + + if (ref $invocant){ + $invocant->{'product'} = $product; + return $product->id; + } + + return $product; +} +sub _check_isactive { + my ($invocant, $isactive) = @_; + ThrowCodeError('bad_arg', {argument => 'isactive', function => 'set_isactive'}) unless ($isactive =~ /(1|0)/); + return $isactive; +} +sub _check_name { + my ($invocant, $name, $product_id) = @_; + + $name = clean_text($name) if $name; + + if (!defined $name || $name eq '') { + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}); + } + + trick_taint($name); + + # Check that we don't already have a environment with that name in this product. + my $orig_id = check_environment($name, $product_id); + my $notunique; + + if (ref $invocant){ + # If updating, we have matched ourself at least + $notunique = 1 if (($orig_id && $orig_id != $invocant->id)) + } + else { + # In new environment any match is one too many + $notunique = 1 if $orig_id; + } + + ThrowUserError('testopia-name-not-unique', + {'object' => 'Environment', + 'name' => $name}) if $notunique; + + return $name; +} + +############################### +#### Mutators #### +############################### +sub set_isactive { $_[0]->set('isactive', $_[1]); } +sub set_name { + my ($self, $value) = @_; + $value = $self->_check_name($value, $self->product_id); + $self->set('name', $value); +} + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (!keys %$param || $param->{PREVALIDATED}){ + bless($param, $class); + return $param; + } + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $product = $params->{product_id}; + + $params->{name} = $class->_check_name($params->{name}, ref $product ? $product->id : $product); + + return $params; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{isactive} = 1; + $field_values->{product_id} = ref $field_values->{product_id} ? $field_values->{product_id}->id : $field_values->{product_id}; + my $self = $class->SUPER::insert_create_data($field_values); + + return $self; +} + +# variables used for create_full() to _parseElementsRecursively() inter-subroutine communication: +my @environment_map; +my $modified_environment_structure = 0; + +sub create_full { + my $self = shift; + my ($env_basename, $prod_id, $environment) = @_; + + # first, get ALL rows to add to test_environment_map table + # and store them in @environment_map array + foreach my $key (keys(%{$environment})){ + require Bugzilla::Testopia::Environment::Category; + my $cat = Bugzilla::Testopia::Environment::Category->new({'product_id' => $prod_id}); + my $cat_id = $cat->check_category($key); + if (!$cat_id) { warn "category: $key for id: $cat did not exist"; return 0; } + _parseElementsRecursively($environment->{$key}, $cat_id, 'category'); + } + + # if we didn't touch the underlying element/property structure: + # see if an existing environment matches + if(!$modified_environment_structure){ + # environment must begin with $env_basename + my $dbh = Bugzilla->dbh; + my @envmatch = @{$dbh->selectcol_arrayref( + "SELECT environment_id + FROM test_environments + WHERE name LIKE '$env_basename:%' AND product_id = $prod_id")}; + + if(scalar(@envmatch)){ + # ALL rows must be represented in that environment + foreach my $hash (@environment_map) { + my $env_id_conditions = "environment_id = " . pop @envmatch; + foreach(@envmatch){$env_id_conditions .= " OR environment_id = $_";} + + @envmatch = @{$dbh->selectcol_arrayref( + "SELECT environment_id + FROM test_environment_map + WHERE ( $env_id_conditions ) AND + property_id = $hash->{prop_id} AND + element_id = $hash->{elem_id} AND + value_selected = '$hash->{value_selected}'")}; + last if (!scalar(@envmatch)); + } + + # if we got at least one match.. + if(scalar(@envmatch)){ + # choose the highest valued one (most recent) and return it + my $max = pop @envmatch; + foreach(@envmatch){ + $max = $_ if $_ > $max;} + return $max; + } + } + } + + # else, create a new environment + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); + $year += 1900; + $hour = sprintf("%02d", $hour); $min = sprintf("%02d", $min); $yday = sprintf("%03d", $yday); + my $environment_name = $env_basename . ':' . $year . $yday . ':' . $hour . $min; + + my $env = $self->create({ + name => $environment_name, + product_id => $prod_id + }); + + my $env_id = $env->id; + ThrowUserError('could-not-create-environment', {'environment_name' => $environment_name}) unless $env_id; + + # and create all rows in test_environment_map table for this environment_id + foreach my $hash (@environment_map) { + $env->store_property_value($hash->{prop_id}, $hash->{elem_id}, $hash->{value_selected}); + } + + # return the environment id + return $env_id; +} + +sub _parseElementsRecursively { +# internal function used by create_full() + my ($hash, $callerid, $callertype, $categoryid) = @_; + $categoryid = $callerid if !defined($categoryid); + + foreach my $key (keys(%{$hash})) { + if(ref($hash->{$key})){ # must be element, since property contains value instead of another hash + require Bugzilla::Testopia::Environment::Element; + my $elem = Bugzilla::Testopia::Environment::Element->new({}); + # get exising element OR create new one + my ($elem_id) = $elem->check_element($key, $callerid); + if(!$elem_id){ + $elem->{'env_category_id'} = ($callertype eq 'category') ? $callerid : $categoryid; + $elem->{'name'} = $key; + $elem->{'parent_id'} = ($callertype eq 'element') ? $callerid : 0; + $elem->{'isprivate'} = 0; + ($elem_id) = $elem->store(); + ThrowUserError('could-not-create-element', {'element_name' => $key}) unless $elem_id; + $modified_environment_structure = 1; + } + _parseElementsRecursively($hash->{$key}, $elem_id, 'element', $categoryid); + } else { + require Bugzilla::Testopia::Environment::Property; + my $prop = Bugzilla::Testopia::Environment::Property->new({}); + my ($prop_id) = $prop->check_property($key, $callerid); + # get existing property OR create new one + if(!$prop_id){ + $prop->{'element_id'} = $callerid; + $prop->{'name'} = $key; + $prop->{'validexp'} = $hash->{$key}; + ($prop_id) = $prop->store(); + ThrowUserError('could-not-create-property', {'property_name' => $key}) unless $prop_id; + $modified_environment_structure = 1; + } else { + # if property exists, still update validexp if needed + $prop = Bugzilla::Testopia::Environment::Property->new($prop_id); + my $validexp = $prop->validexp; + if ($validexp !~ m/\Q$hash->{$key}/){ + my $newexp = $validexp . ((!length($validexp)) ? "" : "|") . $hash->{$key}; + $prop->update_property_validexp($newexp); + $modified_environment_structure = 1; + } + } + # push to array which will be used later + push @environment_map, {prop_id => $prop_id, elem_id => $callerid, value_selected => $hash->{$key}}; + } + } +} + +############################### +#### Methods #### +############################### + +=head1 METHODS + +=head2 get element list for environment + +Returns an array of element objects for an environment + +=cut + +sub get_environment_elements{ + my $dbh = Bugzilla->dbh; + my $self = shift; + + return $self->{'elements'} if exists $self->{'elements'}; + + my $id = $self->{'environment_id'}; + + my $ref = $dbh->selectcol_arrayref(" + SELECT DISTINCT tee.element_id + FROM test_environment_map as tem + JOIN test_environment_element as tee + ON tem.element_id = tee.element_id + WHERE tem.environment_id = ?",undef,$id); + + my @elements; + + foreach my $val (@$ref){ + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + $self->{'elements'} = \@elements; + + return \@elements; +} + +sub element_count { + my $self = shift; + + return scalar(@{$self->get_environment_elements}); +} + +sub element_categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT env_category_id + FROM test_environment_element tee + INNER JOIN test_environment_map tem + ON tem.element_id = tee.element_id + WHERE tem.environment_id = ?", undef, $self->id); + + my @elements; + foreach my $val (@$ref){ + push @elements, Bugzilla::Testopia::Environment::Category->new($val); + } + $self->{'categories'} = \@elements; + + return \@elements; +} + +sub categories_to_json { + my $self = shift; + + my @elements_array; + foreach my $category (@{$self->element_categories}){ + push @elements_array, { + text => $category->{'name'}, + id => $category->id, + type => 'category', + leaf => JSON::false, + cls => 'category', + draggable => JSON::false, + }; + } + + my $json = new JSON; + print $json->encode(\@elements_array); + return undef; +} + +sub mapped_category_elements_to_json { + my $self = shift; + my ($cat_id) = @_; + + my $dbh = Bugzilla->dbh; + + trick_taint($cat_id); + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT tem.element_id + FROM test_environment_map tem + INNER JOIN test_environment_element tee + ON tee.element_id = tem.element_id + WHERE tee.env_category_id = ? + AND tee.element_id IN (SELECT element_id + FROM test_environment_map + WHERE environment_id = ?)", + undef, ($cat_id, $self->id)); + + my @elements; + foreach my $id (@$ref){ + my $element = Bugzilla::Testopia::Environment::Element->new($id); + push @elements, { + text => $element->{'name'}, + id => $element->id, + type => 'element', + leaf => $element->check_for_children ? JSON::false : JSON::true, + cls => 'element', + draggable => JSON::false, + }; + } + + my $json = new JSON; + print $json->encode(\@elements); + return undef; + +} + +sub get_value_selected{ + my $dbh = Bugzilla->dbh; + my $self = shift; + + my ($environment,$element,$property) = (@_); + + my ($var) = $dbh->selectrow_array( + "SELECT value_selected + FROM test_environment_map + WHERE environment_id = ? + AND element_id = ? + AND property_id = ?", + undef,($environment,$element,$property)); + + return $var; +} + +sub get_environments{ + my $dbh = Bugzilla->dbh; + my $self = shift; + + my $ref = $dbh->selectall_arrayref( + "SELECT environment_id, name + FROM test_environments"); + + return $ref; +} + +sub get_all_env_categories { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'env_category_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name + FROM test_environment_category", + {'Slice' => {}}); + + return $ref; +} + +sub get_all_visible_elements { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'element_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name + FROM test_environment_element", + {'Slice' => {}}); + + return $ref; +} + +sub get_all_element_properties { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'property_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name, validexp + FROM test_environment_property", + {'Slice' => {}}); + + return $ref; +} + +sub get_distinct_property_values { + my $self = shift; + my @exps; + foreach my $prop (@{$self->get_all_element_properties}){ + push @exps, split(/\|/, $prop->{'validexp'}) + } + my %seen; + foreach my $v (@exps){ + $seen{$v} = $v; + } + my @values; + foreach my $v (keys %seen){ + my %p; + $p{'id'} = $v; + $p{'name'} = $v; + push @values, \%p; + } + return \@values; +} + +=head2 get all elements + +Returns the list of element names, ids and category names + +=cut + +sub get_all_elements{ + + my $dbh = Bugzilla->dbh; + my $self = shift; + + my $ref = $dbh->selectcol_arrayref( + "SELECT tee.element_id + FROM test_environment_map as tem + JOIN test_environment_element as tee + ON tem.element_id = tee.element_id", + undef); + + my @elements; + + foreach my $val (@$ref){ + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + + return \@elements; +} + +sub check_environment{ + my ($name, $product, $throw) = (@_); + my $pid = ref $product ? $product->id : $product; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environments + WHERE name = ? AND product_id = ?", + undef, ($name, $pid)); + if ($throw){ + ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $name}) unless $used; + return Bugzilla::Testopia::Environment->new($used); + } + + return $used; +} + +sub check_value_selected { + my $self = shift; + my ($prop_id, $elem_id) = @_; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environment_map + WHERE environment_id = ? + AND property_id = ? + AND element_id = ?", + undef, ($self->{'environment_id'}, $prop_id, $elem_id)); + + return $used; +} + +=head2 store property values + +Serializes the property values to the database + +=cut + +sub store_property_value { + my $self = shift; + + my ($prop_id,$elem_id,$value_selected) = @_; + + return 0 if ($self->check_value_selected($prop_id, $elem_id, $value_selected)); + + my $dbh = Bugzilla->dbh; + $dbh->do("INSERT INTO test_environment_map (environment_id,property_id,element_id,value_selected) + VALUES (?,?,?,?)",undef, ($self->{'environment_id'}, $prop_id, $elem_id,$value_selected)); + return 1; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + $obj->{'product_name'} = $self->product->name if $self->product; + $obj->{'case_run_count'} = $self->case_run_count; + $obj->{'run_count'} = $self->get_run_count; + + return $json->encode($obj); +} +=head2 update + +Updates this environment object in the database. +Takes a reference to a hash whose keys match the fields of +an environment. + +=cut + +sub update { + my $self = shift; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); + + $self->SUPER::update(); + + $dbh->bz_commit_transaction(); + + my $elements = $self->{'elements'}; + + foreach my $element (@$elements) + { + $self->persist_environment_element_and_children(1, $element, "update"); + } + + return 1; +} + + +=head2 persist_environment_element_and_children + +Persists Environment Element and Children Recursively. + +=cut + +sub persist_environment_element_and_children { + my $self = shift; + my ($depth, $element, $method) = @_; + if ($depth > $max_depth) { + return; + } + $depth++; + my $elem_id = $element->{'element_id'}; + my $properties = $element->{'properties'}; + foreach my $property (@$properties) + { + my $prop_id = $property->{'property_id'}; + my $value_selected = $property->{'value_selected'}; + my ($value_stored) = $self->get_value_selected($self->{'environment_id'},$prop_id,$elem_id); + if ($method eq "store" || $value_stored eq undef) { + $self->store_property_value($prop_id,$elem_id,$value_selected); + } + else { + $self->update_property_value($prop_id,$elem_id,$value_selected); + } + } + my $children = $element->{'children'}; + foreach my $child_element (@$children) { + $self->persist_environment_element_and_children($depth, $child_element, $method); + } +} + + +=head2 update property value + +Updates the property of the element in the database + +=cut + +sub update_property_value { + my $self = shift; + my ($propID, $elemID, $valueSelected) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do("UPDATE test_environment_map + SET value_selected = ? WHERE environment_id = ? AND property_id = ? AND element_id = ?" + ,undef, ($valueSelected,$self->{'environment_id'},$propID,$elemID)); + return 1; +} + +=head2 toggle_hidden + +Toggles the archive bit on the build. + +=cut + +sub toggle_archive { + my $self = shift; + my $dbh = Bugzilla->dbh; + $dbh->do("UPDATE test_environments SET isactive = ? + WHERE environment_id = ?", undef, $self->isactive ? 0 : 1, $self->id); + +} + +sub delete_element { + my $self = shift; + my ($element_id) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM test_environment_map + WHERE environment_id = ? AND element_id = ?", + undef,($self->id, $element_id)); + +} + +sub element_is_mapped{ + my $self = shift; + my ($element_id) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->selectcol_arrayref("SELECT * FROM test_environment_map + WHERE environment_id = ? AND element_id = ?", + undef,($self->id, $element_id)); + + if($dbh) + { + return 1; + } + + return 0; + +} + +=head2 obliterate + +Completely removes this environment from the database. + +=cut + +sub obliterate { + my $self = shift; + return 0 unless $self->candelete; + my $dbh = Bugzilla->dbh; + + foreach my $obj (@{$self->runs}){ + $obj->obliterate; + } + foreach my $obj (@{$self->caseruns}){ + $obj->obliterate; + } + + $dbh->do("DELETE FROM test_environment_map WHERE environment_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_environments WHERE environment_id = ?", undef, $self->id); + return 1; +} + +sub clone { + my $self = shift; + my ($name, $product) = @_; + my $dbh = Bugzilla->dbh; + + my $new = $self->create({ + name => $name, + product_id => $product + }); + + my $ref = $dbh->selectall_arrayref( + "SELECT * FROM test_environment_map + WHERE environment_id = ?",{'Slice' => {}}, $self->id); + + my $sth = $dbh->prepare_cached( + "INSERT INTO test_environment_map + (environment_id, property_id, element_id, value_selected) + VALUES (?,?,?,?)"); + + foreach my $row (@$ref){ + $sth->execute($new->id, $row->{'property_id'}, $row->{'element_id'}, $row->{'value_selected'}) + } + + return $new->id; +} + +=head2 get_run_list + +Returns a list of run ids associated with this environment. + +=cut + +sub get_run_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs + WHERE environment_id = ?", + undef, $self->{'environment_id'}); + return join(",", @{$ref}); +} + +=head2 get_run_count + +Returns a count of the runs associated with this environment + +=cut + +sub get_run_count { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($count) = $dbh->selectrow_array("SELECT COUNT(run_id) FROM test_runs + WHERE environment_id = ?", + undef, $self->{'environment_id'}); + return $count; +} + +=head2 canedit + +Returns true if the logged in user has rights to edit this environment. + +=cut + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->user->can_see_product($self->product->name); + return 0; +} + +=head2 canview + +Returns true if the logged in user has rights to view this environment. + +=cut + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->can_see_product($self->product->name); + return 0; +} + +=head2 candelete + +Returns true if the logged in user has rights to delete this environment. + +=cut + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group("admin"); + return 0 unless Bugzilla->params->{"allow-test-deletion"}; + return 0 unless Bugzilla->user->can_see_product($self->product->name); + return 1 if Bugzilla->user->in_group("Testers") && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + return 0; +} + +############################### +#### Accessors #### +############################### +=head2 id + +Returns the ID of this object + +=head2 name + +Returns the name of this object + +=cut + +sub id { return $_[0]->{'environment_id'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub isactive { return $_[0]->{'isactive'}; } +sub name { return $_[0]->{'name'}; } + +=head2 product + +Returns the bugzilla product + +=cut + +sub product { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'product'} if exists $self->{'product'}; + require Bugzilla::Testopia::Product; + $self->{'product'} = Bugzilla::Product->new($self->{'product_id'}); + return $self->{'product'}; +} + +=head2 runs + +Returns a reference to a list of test runs useing this environment + +=cut + +sub runs { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'runs'} if exists $self->{'runs'}; + + require Bugzilla::Testopia::TestRun; + + my $runids = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs + WHERE environment_id = ?", + undef, $self->id); + my @runs; + foreach my $id (@{$runids}){ + push @runs, Bugzilla::Testopia::TestRun->new($id); + } + + $self->{'runs'} = \@runs; + return $self->{'runs'}; +} + +=head2 caseruns + +Returns a reference to a list of test caseruns useing this environment + +=cut + +sub caseruns { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'caseruns'} if exists $self->{'caseruns'}; + + require Bugzilla::Testopia::TestCaseRun; + + my $ids = $dbh->selectcol_arrayref("SELECT case_run_id FROM test_case_runs + WHERE environment_id = ?", + undef, $self->id); + my @caseruns; + foreach my $id (@{$ids}){ + push @caseruns, Bugzilla::Testopia::TestCaseRun->new($id); + } + + $self->{'caseruns'} = \@caseruns; + return $self->{'caseruns'}; +} + +sub case_run_count { + my ($self,$status_id) = @_; + my $dbh = Bugzilla->dbh; + + my $query = "SELECT COUNT(case_run_id) FROM test_case_runs + WHERE environment_id = ?"; + $query .= " AND case_run_status_id = ?" if $status_id; + + my $count; + if ($status_id){ + $count = $dbh->selectrow_array($query, undef, ($self->id,$status_id)); + } + else { + $count = $dbh->selectrow_array($query, undef, $self->id); + } + + return $count; +} + +sub type { + my $self = shift; + $self->{'type'} = 'environment'; + return $self->{'type'}; +} + +=head1 TODO + + +=head1 SEE ALSO + +TestRun + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; diff --git a/Bugzilla/Testopia/Environment/Category.pm b/Bugzilla/Testopia/Environment/Category.pm new file mode 100644 index 0000000..e36b1bf --- /dev/null +++ b/Bugzilla/Testopia/Environment/Category.pm @@ -0,0 +1,544 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden + +=head1 NAME + +Bugzilla::Testopia::Environment::Category - A test element category + +=head1 DESCRIPTION + +Categories are used to organize environment elements. + +=head1 SYNOPSIS + + $prop = Bugzilla::Testopia::Environment::Category->new($env_category_id); + $prop = Bugzilla::Testopia::Environment::Category->new(\%cat_hash); + +=cut + +package Bugzilla::Testopia::Environment::Category; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Config; +use Bugzilla::User; +use Bugzilla::Constants; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Product; + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + env_category_id + product_id + name + +=cut + +use constant DB_COLUMNS => qw( + env_category_id + product_id + name +); + +############################### +#### Methods #### +############################### + +=head1 METHODS + +=head2 new + +Instantiates a new Category object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $self = {}; + bless( $self, $class ); + return $self->_init(@_); +} + +=head2 _init + +Private constructor for the category class + +=cut + +sub _init { + my $self = shift; + my ($param) = (@_); + my $dbh = Bugzilla->dbh; + my $columns = join( ", ", DB_COLUMNS ); + + my $id = $param unless ( ref $param eq 'HASH' ); + my $obj; + + if ( defined $id && detaint_natural($id) ) { + + $obj = $dbh->selectrow_hashref( + qq{ + SELECT $columns + FROM test_environment_category + WHERE env_category_id = ?}, undef, $id + ); + } + elsif ( ref $param eq 'HASH' ) { + $obj = $param; + } + + return undef unless ( defined $obj ); + + foreach my $field ( keys %$obj ) { + $self->{$field} = $obj->{$field}; + } + return $self; +} + +=head2 get element list by category + +Returns an array of element objects for a category + +=cut + +sub get_elements_by_category { + my $self = shift; + + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + qq{ + SELECT element_id + FROM test_environment_element + WHERE env_category_id = ?}, undef, $self->{'env_category_id'} + ); + + my @elements; + + foreach my $val (@$ref) { + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + + return \@elements; +} + +=head2 get_parent_elements + +Returns an array of parent elements by category + +=cut + +sub get_parent_elements { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + qq{ + SELECT element_id + FROM test_environment_element + WHERE env_category_id = ? AND (parent_id is null or parent_id = 0) }, + undef, $self->{'env_category_id'} + ); + + my @elements; + + foreach my $val (@$ref) { + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + + return \@elements; +} + +=head2 check_for_elements + +Returns 1 if a category has any elements + +=cut + +sub check_for_elements { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectrow_array( + qq{ + SELECT 1 + FROM test_environment_element + WHERE env_category_id = ?}, undef, $self->{'env_category_id'} + ); + + return $ref; +} + +sub is_mapped { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($ref) = $dbh->selectrow_array( + "SELECT tee.element_id + FROM test_environment_map tem + INNER JOIN test_environment_element tee on tee.element_id = tem.element_id + WHERE tee.env_category_id = ?", undef, $self->id + ); + + return $ref; +} + + +=head2 get_product_list + +Returns the product_id, product name, and count of categories + +=cut + +sub get_env_product_list { + my $self = shift; + my ($class_id) = @_; + + my $dbh = Bugzilla->dbh; + my $query = "SELECT p.id, p.name, COUNT(tec.env_category_id) AS cat_count + FROM products p + LEFT JOIN group_control_map + ON group_control_map.product_id = p.id "; + + if ( Bugzilla->params->{'useentrygroupdefault'} ) { + $query .= "AND group_control_map.entry != 0 "; + } + else { + $query .= + "AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . " "; + } + if ( %{ Bugzilla->user->groups } ) { + $query .= "AND group_id NOT IN(" + . join( ',', values( %{ Bugzilla->user->groups } ) ) . ") "; + } + + $query .= "LEFT OUTER JOIN test_environment_category AS tec + ON p.id = tec.product_id "; + $query .= "WHERE group_id IS NULL "; + $query .= "AND classification_id = ? " if $class_id; + $query .= $dbh->sql_group_by( "p.id", "p.name" ); + $query .= " ORDER BY p.name"; + + my $ref; + if ($class_id) { + $ref = $dbh->selectall_arrayref( $query, { 'Slice' => {} }, $class_id ); + } + else { + $ref = $dbh->selectall_arrayref( $query, { 'Slice' => {} } ); + } + unshift @$ref, + { + 'id' => 0, + 'name' => '--ANY PRODUCT--', + 'cat_count' => $self->get_all_child_count + }; + return $ref; + +} + +sub get_all_child_count { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($all_count) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_environment_category + WHERE product_id = 0" + ); + + return $all_count; +} + +sub product_categories_to_json { + my $self = shift; + my ( $product_id ) = @_; + detaint_natural($product_id); + my $json = new JSON; + + my $categories = $self->get_element_categories_by_product($product_id); + + my @values; + + foreach my $cat (@$categories) { + push @values, + { + text => $cat->{'name'}, + id => $cat->id, + leaf => $cat->check_for_elements ? JSON::false : JSON::true, + type => 'category', + cls => 'category' + }; + } + + return $json->encode( \@values ); +} + +=head2 get_element_categories_by_product + +Returns the list of element category names and ids by product id + +=cut + +sub get_element_categories_by_product { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($product_id) = (@_); + + my $ref = $dbh->selectcol_arrayref( + "SELECT env_category_id + FROM test_environment_category + WHERE product_id = ?", + undef, $product_id + ); + my @objs; + foreach my $id ( @{$ref} ) { + push @objs, Bugzilla::Testopia::Environment::Category->new($id); + } + return \@objs; +} + +=head2 new_category_count + +Returns 1 if element has children + +=cut + +sub new_category_count { + my $self = shift; + my ($prod_id) = @_; + $prod_id ||= $self->{'product_id'}; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_environment_category + WHERE name like 'New category%' + AND product_id = ?", + undef, $prod_id + ); + + return $used + 1; +} + +sub elements_to_json { + my $self = shift; + + my @values; + foreach my $element (@{$self->get_parent_elements}) { + push @values, + { + text => $element->name, + id => $element->id, + type => 'element', + leaf => $element->check_for_children ? JSON::false : JSON::true, + cls => 'element' + }; + } + + my $json = new JSON(); + return $json->encode( \@values ); +} + +=head2 check_category + +Returns category id if a category exists + +=cut + +sub check_category { + my $dbh = Bugzilla->dbh; + my $self = shift; + my ( $name, $prodID ) = (@_); + + $prodID ||= $self->product_id; + + my ($used) = $dbh->selectrow_array( + "SELECT env_category_id + FROM test_environment_category + WHERE name = ? AND product_id = ?", + undef, ( $name, $prodID ) + ); + + return $used; +} + +=head2 store + +Serializes this category to the database and returns the key or 0 + +=cut + +sub store { + my $self = shift; + + # Exclude the auto-incremented field from the column list. + my $columns = join( ", ", grep { $_ ne 'env_category_id' } DB_COLUMNS ); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + return 0 if $self->check_category( $self->{'name'}, $self->{'product_id'} ); + + my $dbh = Bugzilla->dbh; + $dbh->do( "INSERT INTO test_environment_category ($columns) VALUES (?, ?)", + undef, ( $self->{'product_id'}, $self->{'name'} ) ); + my $key = + $dbh->bz_last_key( 'test_environment_category', 'env_category_id' ); + + return $key; +} + +=head2 set_name + +Updates the category name in the database + +=cut + +sub set_name { + my $self = shift; + my ($name) = (@_); + my $dbh = Bugzilla->dbh; + + return undef if $self->check_category($name); + + $dbh->do( + "UPDATE test_environment_category SET name = ? + WHERE env_category_id = ? AND product_id = ?", + undef, ( $name, $self->{'env_category_id'}, $self->{'product_id'} ) + ); + return 1; +} + +=head2 set_product + +Updates the category in the database + +=cut + +sub set_product { + my $self = shift; + my ($product_id) = (@_); + + return if ( $product_id == $self->{'product_id'} ); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_category SET product_id = ? + WHERE env_category_id = ? AND product_id = ?", + undef, + ( $product_id, $self->{'env_category_id'}, $self->{'product_id'} ) + ); + return 1; +} + +=head2 obliterate + +Completely removes the element category entry from the database. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $children = $dbh->selectcol_arrayref( + "SELECT element_id FROM test_environment_element + WHERE env_category_id = ?", undef, $self->id + ); + + foreach my $id (@$children) { + my $element = Bugzilla::Testopia::Environment::Element->new($id); + $element->obliterate; + } + $dbh->do( "DELETE FROM test_environment_category WHERE env_category_id = ?", + undef, $self->id ); + return 1; + +} + +sub canview { + my $self = shift; + return 1 if ( $self->product_id == 0 ); + return 1 if Bugzilla->user->can_see_product( $self->product->name ); + return 0; +} + +sub canedit { + my $self = shift; + if ( $self->product_id == 0 ) { + return 1 if Bugzilla->user->in_group('Testers'); + return 0; + } + return 1 if $self->product->canedit; + return 0; +} + +sub candelete { + my $self = shift; + return 0 unless $self->canedit; +} + +############################### +#### Accessors #### +############################### + +=head2 id + +Returns the ID of this category + +=head2 name + +Returns the name of this category + +=head2 product_id + +Returns the product_id of this category + +=cut + +sub id { return $_[0]->{'env_category_id'}; } +sub name { return $_[0]->{'name'}; } +sub product_id { return $_[0]->{'product_id'}; } + +sub product { + my $self = shift; + + $self->{'product'} = Bugzilla::Testopia::Product->new( $self->product_id ); + return $self->{'product'}; +} + +=head2 type + +Returns 'env_category' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'env_category'; + return $self->{'type'}; +} +1; diff --git a/Bugzilla/Testopia/Environment/Element.pm b/Bugzilla/Testopia/Environment/Element.pm new file mode 100644 index 0000000..f881517 --- /dev/null +++ b/Bugzilla/Testopia/Environment/Element.pm @@ -0,0 +1,573 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden +# Andrew Nelson + +=head1 NAME + +Bugzilla::Testopia::Environment::Element - A test environment element + +=head1 DESCRIPTION + +Environment elements are a set of environment entities that dictate +the conditions a test was conducted in. Each environment must have an +element. Elements can be very simple or very complex by adding properties. +Elements can have child elements. + +=head1 SYNOPSIS + + $elem = Bugzilla::Testopia::Environment::Element->new($elem_id); + $elem = Bugzilla::Testopia::Environment::Element->new(\%elem_hash); + +=cut + +package Bugzilla::Testopia::Environment::Element; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::User; +use Bugzilla::Testopia::Product; +use JSON; + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + element_id + env_category_id + name + parent_id + isprivate + +=cut + +use constant DB_COLUMNS => qw( + element_id + env_category_id + name + parent_id + isprivate +); + +our constant $max_depth = 5; + +############################### +#### Methods #### +############################### + +=head1 METHODS + +=head2 new + +Instantiates a new Element object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $self = {}; + bless( $self, $class ); + return $self->_init(@_); +} + +=head2 _init + +Private constructor for the element class + +=cut + +sub _init { + my $self = shift; + my ($param) = (@_); + my $dbh = Bugzilla->dbh; + my $columns = join( ", ", DB_COLUMNS ); + + my $id = $param unless ( ref $param eq 'HASH' ); + my $obj; + + if ( defined $id && detaint_natural($id) ) { + + $obj = $dbh->selectrow_hashref( + "SELECT $columns + FROM test_environment_element + WHERE element_id = ?", undef, $id + ); + } + elsif ( ref $param eq 'HASH' ) { + $obj = $param; + } + + return undef unless ( defined $obj ); + + foreach my $field ( keys %$obj ) { + $self->{$field} = $obj->{$field}; + } + + $self->get_properties(); + + return $self; +} + +sub is_mapped { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($ref) = $dbh->selectrow_array( + "SELECT element_id + FROM test_environment_map + WHERE element_id = ?", undef, $self->id + ); + + return $ref; +} + +=head2 get_child_elements + +Returns an array of the children objects for an element and recursively +gets all of their children until exhausted. + +=cut + +sub get_child_elements { + my $dbh = Bugzilla->dbh; + my $self = shift; + + return $self->{'children'} if exists $self->{'children'}; + + my %newvalues = (@_); + my $depth = $max_depth; + + if (%newvalues) { + $depth = $newvalues{'depth'}; + } + + $depth--; + + if ( $depth == 0 ) { return; } + + my $id = $self->{'element_id'}; + + my $ref = $dbh->selectcol_arrayref( + qq{ + SELECT tee.element_id + FROM test_environment_element as tee + WHERE tee.parent_id = ?}, undef, $id + ); + + my @children; + + foreach my $val (@$ref) { + my $child = Bugzilla::Testopia::Environment::Element->new($val); + $child->get_child_elements( 'depth' => $depth ); + push( @children, $child ); + } + + $self->{'children'} = \@children; + +} + +=head2 get_properties + +Returns an array of the property objects for an element. + +=cut + +sub get_properties { + my $dbh = Bugzilla->dbh; + my $self = shift; + + my $ref = $dbh->selectcol_arrayref( + qq{ + SELECT tep.property_id + FROM test_environment_property as tep + WHERE tep.element_id = ?}, undef, ( $self->{'element_id'} ) + ); + + my @properties; + + foreach my $val (@$ref) { + my $property = Bugzilla::Testopia::Environment::Property->new($val); + push( @properties, $property ); + } + + $self->{'properties'} = \@properties; + return $self->{'properties'}; + +} + +=head2 check_element + +Returns element id if element exists + +=cut + +sub check_element { + my $dbh = Bugzilla->dbh; + my $self = shift; + my ( $name, $cat_id ) = @_; + +# Since categories are uniquely identified by product_id we don't have to check by join on the product_id. + my ($used) = $dbh->selectrow_array( + "SELECT element_id + FROM test_environment_element + WHERE name = ? + AND env_category_id = ?", + undef, ( $name, $cat_id ) + ); + + return $used; +} + +=head2 check_for_children + +Returns 1 if element has children + +=cut + +sub check_for_children { + my $dbh = Bugzilla->dbh; + my $self = shift; + + my ($has_element) = $dbh->selectrow_array( + "SELECT 1 + FROM test_environment_element + WHERE parent_id = ?", + undef, $self->{'element_id'} + ); + + my ($has_property) = $dbh->selectrow_array( + "SELECT 1 + FROM test_environment_property + WHERE element_id = ?", + undef, $self->{'element_id'} + ); + + return $has_element || $has_property; +} + +sub children_to_json { + my $self = shift; + my $json = new JSON; + + my @values; + foreach my $element ( @{ $self->get_child_elements } ) { + push @values, + { + text => $element->{'name'}, + id => $element->{'element_id'}, + leaf => $element->check_for_children ? JSON::false : JSON::true, + type => 'element', + cls => 'element', + draggable => JSON::true, + }; + } + + foreach my $property (@{$self->get_properties}) { + push @values, + { + text => $property->{'name'}, + id => $property->id, + leaf => $property->check_for_validexp ? JSON::false : JSON::true, + type => 'property', + cls => 'property', + draggable => JSON::false, + }; + } + + return $json->encode( \@values ); +} + +=head2 new_element_count + +Returns 1 if element has children + +=cut + +sub new_element_count { + my $self = shift; + my ($cat_id) = @_; + $cat_id ||= $self->{'env_category_id'}; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_environment_element + WHERE name like 'New element%' + AND env_category_id = ?", + undef, $cat_id + ); + + return $used + 1; +} + +sub this_to_json { + my $self = shift; + + my $element = { + text => $self->{'name'}, + id => $self->{'element_id'}, + type => 'element', + leaf => JSON::true, + cls => 'element', + allowDrop => JSON::false, + disabled => JSON::true + }; + my $json = new JSON(); + print $json->encode($element); + +} + +=head2 check_for_properties + +Returns 1 if element has properties + +=cut + +sub check_for_properties { + my $dbh = Bugzilla->dbh; + my $self = shift; + + my ($used) = $dbh->selectrow_array( + qq{ + SELECT 1 + FROM test_environment_property + WHERE element_id = ? }, undef, $self->{'element_id'} + ); + + return $used; +} + +=head2 store + +Serializes the new element to the database and returns the primary key or 0 + +=cut + +sub store { + my $self = shift; + + # Exclude the auto-incremented field from the column list. + my $columns = join( ", ", grep { $_ ne 'element_id' } DB_COLUMNS ); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + # Verify name is available + return undef + if $self->check_element( $self->{'name'}, $self->{'env_category_id'} ); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "INSERT INTO test_environment_element ($columns) VALUES (?,?,?,?)", + undef, + ( + $self->{'env_category_id'}, $self->{'name'}, + $self->{'parent_id'}, $self->{'isprivate'} + ) + ); + my $key = $dbh->bz_last_key( 'test_environment_element', 'element_id' ); + return $key; +} + +=head2 set_name + +Updates the element in the database + +=cut + +sub set_name { + my $self = shift; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + my ($name) = (@_); + + return 0 if check_element( $name, $self->{'env_category_id'} ); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_element + SET name = ? WHERE element_id = ?", undef, + ( $name, $self->{'element_id'} ) + ); + return 1; +} + +=head2 update_element_category + +Updates the category of the element in the database + +=cut + +sub update_element_category { + my $self = shift; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + my ($catid) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_element + SET env_category_id = ? WHERE element_id = ?", undef, + ( $catid, $self->{'element_id'} ) + ); + return 1; +} + +=head2 update_element_parent + +Updates the parent_id of the element in the database + +=cut + +sub update_element_parent { + my $self = shift; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + my ($parent_id) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_element + SET parent_id = ? WHERE element_id = ?", undef, + ( $parent_id, $self->{'element_id'} ) + ); + return 1; +} + +=head2 obliterate + +Completely removes the element entry from the database. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + + foreach my $p ( @{ $self->get_properties } ) { + $p->obliterate; + } + + $dbh->do( + "DELETE FROM test_environment_map + WHERE element_id = ?", undef, $self->id + ); + $dbh->do( + "DELETE FROM test_environment_element + WHERE element_id = ?", undef, $self->{'element_id'} + ); + + return 1; +} + +sub canview { + my $self = shift; + return 1 if $self->get_parent->canview; + return 0; +} + +sub canedit { + my $self = shift; + return 1 if $self->get_parent->canedit; + return 0; +} + +sub candelete { + my $self = shift; + return 0 unless $self->canedit; + +} + +############################### +#### Accessors #### +############################### + +=head2 id + +Returns the ID of this object + +=head2 name + +Returns the name of this object + +=head2 product_id + +Returns the product_id of this object + +=head2 env_category_id + +Returns the category_id associated with this element + +=head2 parent_id + +Returns the element's parent_id associated with this element + + +=cut + +sub id { return $_[0]->{'element_id'}; } +sub name { return $_[0]->{'name'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub env_category_id { return $_[0]->{'env_category_id'}; } +sub parent_id { return $_[0]->{'parent_id'}; } +sub isprivate { return $_[0]->{'isprivate'}; } + +sub get_parent { + my $self = shift; + if ( $self->{'parent_id'} ) { + return $self->new( $self->{'parent_id'} ); + } + else { + return Bugzilla::Testopia::Environment::Category->new( + $self->{'env_category_id'} ); + } +} + +sub is_parent_a_category { + my $self = shift; + if ( $self->{'parent_id'} ) { + return 0; + } + return 1; +} + +sub product { + my $self = shift; + return $self->{'product'} if exists $self->{'product'}; + $self->{'product'} = Bugzilla::Testopia::Product->new( $self->product_id ); + return $self->{'product'}; +} + +=head2 type + +Returns 'element' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'element'; + return $self->{'type'}; +} +1; diff --git a/Bugzilla/Testopia/Environment/Property.pm b/Bugzilla/Testopia/Environment/Property.pm new file mode 100644 index 0000000..a4057f3 --- /dev/null +++ b/Bugzilla/Testopia/Environment/Property.pm @@ -0,0 +1,439 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden + +=head1 NAME + +Bugzilla::Testopia::Environment::Property - A test environment element property + +=head1 DESCRIPTION + +Element properties describe the elements in more detail. An +element can have an unlimited number of properties to describe it. +The valid expression limits the possible descriptions to valid choices +for each property. + +=head1 SYNOPSIS + + $prop = Bugzilla::Testopia::Environment::Property->new($prop_id); + $prop = Bugzilla::Testopia::Environment::Property->new(\%prop_hash); + +=cut + +package Bugzilla::Testopia::Environment::Property; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::User; +use Bugzilla::Testopia::Environment::Element; + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + property_id + element_id + name + validexp + +=cut + +use constant DB_COLUMNS => qw( + property_id + element_id + name + validexp +); + +############################### +#### Methods #### +############################### + +=head1 METHODS + +=head2 new + +Instantiates a new Property object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $self = {}; + bless( $self, $class ); + return $self->_init(@_); +} + +=head2 _init + +Private constructor for the property class + +=cut + +sub _init { + my $self = shift; + my ($param) = (@_); + my $dbh = Bugzilla->dbh; + my $columns = join( ", ", DB_COLUMNS ); + + my $id = $param unless ( ref $param eq 'HASH' ); + my $obj; + + if ( defined $id && detaint_natural($id) ) { + + $obj = $dbh->selectrow_hashref( + qq{ + SELECT $columns + FROM test_environment_property + WHERE property_id = ?}, undef, $id + ); + } + elsif ( ref $param eq 'HASH' ) { + $obj = $param; + } + + return undef unless ( defined $obj ); + + foreach my $field ( keys %$obj ) { + $self->{$field} = $obj->{$field}; + } + return $self; +} + +=head2 check_property + +Returns id if a property exists + +=cut + +sub check_property { + my $self = shift; + my ( $name, $element_id ) = @_; + my $dbh = Bugzilla->dbh; + + unless ( $name && $element_id ) { + return "check_product must be passed a valid name and product_id"; + } + + my ($used) = $dbh->selectrow_array( + qq{ + SELECT property_id + FROM test_environment_property + WHERE name = ? AND element_id = ?}, undef, $name, $element_id + ); + + return $used; +} + +=head2 check_for_validexp + +Returns 1 if a validexp exist for the property + +=cut + +sub check_for_validexp { + my $self = shift; + my $name = (@_); + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + qq{ + SELECT 1 + FROM test_environment_property + WHERE property_id = ? AND (validexp IS NOT NULL AND validexp <> '')}, + undef, $self->{'property_id'} + ); + + return $used; +} + +=head2 new_property_count + +Returns the count + 1 of new properties + +=cut + +sub new_property_count { + my $self = shift; + my ($element_id) = @_; + $element_id ||= $self->{'element_id'}; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_environment_property + WHERE name like 'New property%' + AND element_id = ?", + undef, $element_id + ); + + return $used + 1; +} + +=head2 get_validexp + +Returns the validexp for the property + +=cut + +sub get_validexp { + my $self = shift; + my $name = (@_); + my $dbh = Bugzilla->dbh; + + my ($validexp) = $dbh->selectrow_array( + "SELECT validexp + FROM test_environment_property + WHERE property_id = ?", + undef, $self->{'property_id'} + ); + + return $validexp; +} + +=head2 store + +Serializes the new property to the database + +=cut + +sub store { + my $self = shift; + + # Exclude the auto-incremented field from the column list. + my $columns = join( ", ", grep { $_ ne 'property_id' } DB_COLUMNS ); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + # Verify name is available + return undef + if $self->check_property( $self->{'name'}, $self->{'element_id'} ); + + my $dbh = Bugzilla->dbh; + $dbh->do( "INSERT INTO test_environment_property ($columns) VALUES (?,?,?)", + undef, + ( $self->{'element_id'}, $self->{'name'}, $self->{'validexp'} ) ); + my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' ); + return $key; +} + +=head2 set_name + +Updates the property name in the database + +=cut + +sub set_name { + my $self = shift; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + my ($name) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_property + SET name = ? WHERE property_id = ?", undef, + ( $name, $self->{'property_id'} ) + ); + return 1; +} + +=head2 set_element + +Updates the elmnt_id in the database + +=cut + +sub set_element { + my $self = shift; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + my ($id) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_property + SET element_id = ? WHERE property_id = ?", undef, + ( $id, $self->{'property_id'} ) + ); + return 1; +} + +=head2 update_property_validexp + +Updates the property valid expression in the database + +=cut + +sub update_property_validexp { + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + my $self = shift; + my ($validexp) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE test_environment_property + SET validexp = ? WHERE property_id = ?", undef, + ( $validexp, $self->{'property_id'} ) + ); + return 1; +} + +sub value_to_json { + my $self = shift; + my ($env_id) = @_; + + my $env = Bugzilla::Testopia::Environment->new($env_id) if $env_id; + my @values = split( /\|/, $self->get_validexp ); + + my @json; + foreach (@values) { + my $class; + if ( $env + && $env->get_value_selected( $env->id, $self->element_id, + $self->id ) eq $_ ) + { + $class = "validexpYellow"; + } + else { + $class = "validexp"; + } + push @json, + { + text => $_, + id => " $_", + property_id => $self->id, + type => 'value', + leaf => JSON::true, + cls => $class, + draggable => JSON::false, + }; + } + + my $json = new JSON; + return $json->encode( \@json ); + +} + +=head2 obliterate + +Completely removes the element property entry from the database. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + + $dbh->do( + "DELETE FROM test_environment_map + WHERE property_id = ?", undef, $self->id + ); + $dbh->do( + "DELETE FROM test_environment_property + WHERE property_id = ?", undef, $self->id + ); + + return 1; + +} + +sub is_mapped { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($ref) = $dbh->selectrow_array( + "SELECT element_id + FROM test_environment_map + WHERE property_id = ?", undef, $self->id + ); + + return $ref; +} + + +sub canview { + my $self = shift; + my $element = + Bugzilla::Testopia::Environment::Element->new( $self->element_id ); + return 1 if $element->canview; + return 0; +} + +sub canedit { + my $self = shift; + my $element = + Bugzilla::Testopia::Environment::Element->new( $self->element_id ); + return 1 if $element->canedit; + return 0; +} + +sub candelete { + my $self = shift; + return 0 unless $self->canedit; + +} + +############################### +#### Accessors #### +############################### + +=head2 id + +Returns the ID of this object + +=head2 name + +Returns the name of this object + +=head2 validexp + +Returns the valid expression associated with this property + +=head2 element_id + +Returns the element's id associated with this property + + +=cut + +sub id { return $_[0]->{'property_id'}; } +sub name { return $_[0]->{'name'}; } +sub validexp { return $_[0]->{'validexp'}; } +sub element_id { return $_[0]->{'element_id'}; } + +=head2 type + +Returns 'property' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'property'; + return $self->{'type'}; +} + +1; diff --git a/Bugzilla/Testopia/Environment/Xml.pm b/Bugzilla/Testopia/Environment/Xml.pm new file mode 100644 index 0000000..40044fe --- /dev/null +++ b/Bugzilla/Testopia/Environment/Xml.pm @@ -0,0 +1,585 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Garrett Braden + + +=head1 NAME + +Bugzilla::Testopia::Environment::Xml - An XML representation of the Environment Object. + +=head1 DESCRIPTION + +This module is used to import and export environments via XML. It can parse an XML representation +of a Testopia Environment and persist it to the database. An Environment.pm XML object can be +initialized using new and passing it an XML scalar. It can also take two other parameters: + + $admin - a boolean that automatically will store the imported environment to the database. + $max_depth - the max depth of child elements to import. + +Example: + my $env_xml = Bugzilla::Testopia::Environment::Xml->new($xml, 1, 5); + +Other subroutines can be called on the object. For example: + parse - takes the same three parameters as new + store - stores the imported xml Environment object to the database + +Misc. Other: + the module also contains two other valueable fields on it's hash: + $self->{'message'} - running list of valueable information upon parsing and storing + $self->{'error'} - running list of error messages upon parsing and storing + One other method exists(check_new_items) to check if there are new Elements, Properties, Categories, + and Selected Values and returns a scalar value report of the new items not present in the database. + +Import XML Environment Implementation Example: see tr_import_environment.cgi + +To export an environment by env_id to XML use export + +Example: + my $xml = Bugzilla::Testopia::Environment::Xml->export($env_id); + +Export Environment XML Implementation Example: see tr_export_environment.cgi + +=head1 SYNOPSIS + +use Bugzilla::Testopia::Environment::Xml; + +=cut + +package Bugzilla::Testopia::Environment::Xml; + +#************************************************** Uses ****************************************************# +use strict; +use warnings; +use CGI; +use lib "."; +use XML::Twig; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Product; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Property; + +our constant $max_depth = 7; + + +=head2 new + +Instantiates a new Bugzilla::Testopia::Environment::Xml object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $self = {}; + bless($self, $class); + return $self->_init(@_); +} + + +=head2 _init + +Private constructor for the Bugzilla::Testopia::Environment::XML class + +=cut + +sub _init { + my ($self, $xml, $admin, $depth) = @_; + if (ref $xml eq 'HASH') { + $self = $xml; + } + elsif($xml) { + parse($self, $xml, $admin, $depth); + } + return undef unless (defined $xml); + return $self; +} + + +=head2 Parse Environment + +=head2 DESCRIPTION + +Parses the Environment XML + +=cut + +sub parse() { + my ($self, $xml, $admin, $depth) = @_; + + if($depth eq undef) { + $self->{'max_depth'} = $max_depth; + } + else { + $self->{'max_depth'} = $depth + } + if ($admin) { + $self->{'message'} = "Importing XML Environment...
"; + } + else { + $self->{'message'} = "Parsing and Validating XML Environment...
"; + } + $self->{'error'} = undef; + if ($xml) { + trick_taint($xml); + } + my $twig = XML::Twig->new(); + $twig->parse($xml); + my $root = $twig->root; + # Checking if Product and Environment already exist. + my $product_name = $root->{'att'}->{'product'}; + my $product_id; + if (lc($product_name) eq "--all--") { + $self->{'message'} .= "..Using the --ANY PRODUCT-- PRODUCT.
"; + $product_id = 0; + } + else { + $self->{'message'} .= "..Checking if $product_name PRODUCT already exists..."; + ($product_id) = Bugzilla::Testopia::Product->check_product_by_name($product_name); + if ($product_id) { + $self->{'message'} .= "EXISTS.
"; + } + else { + $self->{'message'} .= "DOESN'T EXIST.
Importing XML Environment Failed!
"; + $self->{'error'} .= "$product_name PRODUCT doesn't exist. Please be sure to use an existing product.
"; + return 0; + } + } + ($self->{'product_id'}) = $product_id; + $self->{'product_name'} = $product_name; + my $environment_name = $root->{'att'}->{'name'}; + $self->{'name'} = $environment_name; + $self->{'message'} .= "..Checking if $environment_name ENVIRONMENT NAME already exists for the $product_name PRODUCT..."; + my $environment = Bugzilla::Testopia::Environment->new({}); + my ($env_id) = $environment->check_environment($environment_name, $product_id); + my $environment_id; + if ($env_id < 1) { + $self->{'message'} .= "DOESN'T EXIST
"; + # Storing New Environment if Admin + if ($admin) { + $self->{'message'} .= "....Storing new $environment_name ENVIRONMENT NAME in the $self->{'product_name'} PRODUCT..."; + $environment->{'name'} = $environment_name; + ($environment_id) = Bugzilla::Testopia::Environment->store_environment_name($self->{'name'}, $product_id); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($environment_id) = $env_id; + $self->{'message'} .= "EXISTS
Importing XML Environment Failed!
"; + $self->{'error'} .= "$environment_name ENVIRONMENT NAME already exists for the $product_name PRODUCT. Please use another name."; + return 0; + } + ($self->{'environment_id'}) = $environment_id; + ($environment->{'product_id'}) = $self->{'product_id'}; + # Parse recursively through the nested child elements. + foreach my $twig_category ($root->children("category")) { + my $category_name = $twig_category->{'att'}->{'name'}; + # Makes sure to get the category_id by name and product_id + my $category = Bugzilla::Testopia::Environment::Category->new({}); + my ($cat_id) = $category->check_category($category_name, $product_id); + my $category_id; + # Checking if Categories already exist. + $self->{'message'} .= "..Checking if $category_name CATEGORY already exists..."; + if ($cat_id < 1) { + $self->{'message'} .= "DOESN'T EXIST.
"; + my $new_category_names = $self->{'new_category_names'}; + push (@$new_category_names, $category_name); + $self->{'new_category_names'} = $new_category_names; + # Storing New Categories if Admin + if ($admin) { + $self->{'message'} .= "....Storing new $category_name CATEGORY in the $self->{'product_name'} PRODUCT..."; + ($category->{'product_id'}) = $product_id; + $category->{'name'} = $category_name; + ($category_id) = $category->store(); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($category_id) = $cat_id; + $self->{'message'} .= "EXISTS.
"; + } + foreach my $twig_element ($twig_category->children("element")) { + my $element = $self->parse_child_elements(1, $category_id, $category_name, $twig_element, $admin); + my $elements = $self->{'elements'}; + push (@$elements, $element); + $self->{'elements'} = $elements; + } + } + if ($admin) { + $self->{'message'} .= "Finished Importing XML Environment!
"; + } + else { + $self->{'message'} .= "Finished Parsing and Validating XML Environment!
"; + } +} + + +=head2 Parse Children Elements + +=head2 DESCRIPTION + +Parses through elements and their children elements recursively + +=cut + +sub parse_child_elements() { + my ($self, $depth, $env_category_id, $category_name, $twig_element, $admin, $parent_element) = @_; + if ($depth > $self->{'max_depth'}) { + return; + } + $depth++; + my $element_name = $twig_element->{'att'}->{'name'}; + # Checking if Elements already exist. + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "Checking if $element_name ELEMENT already exists in the $category_name CATEGORY..."; + my ($product_id) = $self->{'product_id'}; + my $element = Bugzilla::Testopia::Environment::Element->new({}); + my ($elem_id) = $element->check_element($element_name, $env_category_id); + my $element_id; + if ($elem_id < 1) { + $self->{'message'} .= "DOESN'T EXIST.
"; + my $new_category_elements = $self->{'new_category_elements'}; + my $new_category_element = {'env_category_id' => $env_category_id, 'category_name' => $category_name, 'element_name' => $element_name}; + push (@$new_category_elements, $new_category_element); + $self->{'new_category_elements'} = $new_category_elements; + # Storing New Elements if Admin + if ($admin) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "..Storing new $element_name ELEMENT in the $category_name CATEGORY..."; + ($element->{'env_category_id'}) = $env_category_id; + $element->{'name'} = $element_name; + ($element->{'product_id'}) = $self->{'product_id'}; + $element->{'isprivate'} = 0; + if ($parent_element) { + $element->{'parent_id'} = $parent_element->{'element_id'}; + } + ($element_id) = $element->store(); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($element_id) = $elem_id; + $self->{'message'} .= "EXISTS.
"; + } + ($element->{'element_id'}) = $element_id; + ($element->{'env_category_id'}) = $env_category_id; + $element->{'name'} = $element_name; + ($element->{'parent_id'}) = $parent_element->{'parent_id'}; + my @properties; + foreach my $twig_property ($twig_element->children("property")) { + my $property_name = $twig_property->{'att'}->{'name'}; + # Checking if Properties already exist. + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "....Checking if $property_name PROPERTY already exists..."; + my $property = Bugzilla::Testopia::Environment::Property->new({}); + my ($prop_id) = $property->check_property($property_name, $element_id); + my $property_id; + if ($prop_id < 1) { + $self->{'message'} .= "DOESN'T EXIST.
"; + my $new_property_names = $self->{'new_property_names'}; + push (@$new_property_names, $property_name); + $self->{'new_property_names'} = $new_property_names; + # Storing New Property if Admin + if ($admin) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "......Storing new $property_name PROPERTY..."; + $property->{'name'} = $property_name; + ($property->{'element_id'}) = $element_id; + ($property_id) = $property->store(); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($property_id) = $prop_id; + $self->{'message'} .= "EXISTS.
"; + } + $property = Bugzilla::Testopia::Environment::Property->new($property_id); + # Checking if new Selected Value and Valid Expression exist. + my $validexp; + if ($property) { + $validexp = $property->validexp(); + } + my $value = $twig_property->field('value'); + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "........Checking if $value VALUE exists in the list of selectable values..."; + if ( $validexp !~ m/$value/) { + $self->{'message'} .= "DOESN'T EXIST.
"; + if ($admin) { + if (!defined($validexp)) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "..........Setting $value VALID EXPRESSION equal to the VALUE for the first time..."; + $validexp = $value; + } + else { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "..........Adding $value VALUE to the VALID EXPRESSION..."; + $validexp = "$validexp | $value"; + } + $property->update_property_validexp($validexp); + $self->{'message'} .= "DONE.
"; + } + else { + my $new_validexp_values = $self->{'new_validexp_values'}; + my $new_validexp_value = {'property_id' => $property_id, 'property_name' => $property_name, 'value' => $value}; + push (@$new_validexp_values, $new_validexp_value); + $self->{'new_validexp_values'} = $new_validexp_values; + } + } + elsif (!defined($validexp)) { + $self->{'message'} .= "VALID EXPRESSION DOESN'T EXIST YET.
"; + } + else { + $self->{'message'} .= "EXISTS.
"; + } + if ($property_id && $admin) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "............Storing new VALUE SELECTED $value..."; + my $environment = Bugzilla::Testopia::Environment->new($self->{'environment_id'}); + $environment->store_property_value($property_id, $element_id, $value); + $self->{'message'} .= "DONE.
"; + } + $property->{'value_selected'} = $value; + push (@properties, $property); + } + my $elm_properties = $element->{'properties'}; + push (@$elm_properties, @properties); + if ($parent_element) { + my $children = $parent_element->{'children'}; + push (@$children, $element); + $parent_element->{'children'} = $children; + } + foreach my $twig_element_child ($twig_element->children("element")) { + $self->parse_child_elements($depth, $env_category_id, $category_name, $twig_element_child, $admin, $element); + } + return $element; +} + + +=head2 Checking Exists + +=head2 DESCRIPTION + +Checking if the Environment, Elements, Categories, and Properties already exist or not. + +=cut + +sub check_new_items() { + my $self = shift; + my $report; + my $new_category_names = $self->{'new_category_names'}; + foreach my $new_category_name (@$new_category_names) { + $report .= "New $new_category_name CATEGORY.
"; + } + my $new_category_elements = $self->{'new_category_elements'}; + foreach my $new_category_element (@$new_category_elements) { + $report .= "New $new_category_element->{'element_name'} ELEMENT in the "; + if (!$new_category_element->{'env_category_id'}){ + $report .= "new "; + } + $report .= "$new_category_element->{'category_name'} CATEGORY.
"; + } + my $new_property_names = $self->{'new_property_names'}; + foreach my $new_property_name (@$new_property_names) { + $report .= "New $new_property_name PROPERTY.
"; + } + my $new_validexp_values = $self->{'new_validexp_values'}; + foreach my $new_validexp_value (@$new_validexp_values) { + $report .= "New $new_validexp_value->{'value'} VALUE for the "; + if (!$new_validexp_value->{'property_id'}){ + $report .= "new "; + } + $report .= "$new_validexp_value->{'property_name'} PROPERTY's selectable value list.
"; + } + return $report; +} + + +=head2 Store the Environment + +=head2 Description + +Store the Environment Name, Element-Category relationship, Element-Property relationship, +Element-ChildElement relationship, Environment-Element-Property-Value. + +=cut + +sub store() { + my $self = shift; + $self->{'message'} .= "Storing new XML Environment..."; + if (!$self->{'environment_id'}) { + $self->{'environment_id'} = Bugzilla::Testopia::Environment->store_environment_name($self->{'name'}, $self->{'product_id'}); + } + my $environment = Bugzilla::Testopia::Environment->new($self); + my $success = $environment->update(); + if (!$success) { + $self->{'message'} .= "ABORTED!
"; + $self->{'error'} .= "Failed to store the Environment!
"; + return 0; + } + $self->{'message'} .= "DONE!
"; + return 1; +} + + +=head2 export + +=head2 Description + +Exports and Environment by env_id to a scalar XML value + +=cut + +sub export() { + my $self = shift; + my ($env_id) = @_; + + my $xml; + + my $environment = Bugzilla::Testopia::Environment->new($env_id); + + $xml = + "" . + "" . + "get_environment_elements(); + + my $categories = {}; + my $category_names = []; + my $categorized_elements = []; + my $elements = $environment->{'elements'}; + my $used_elements = {}; + foreach my $element (@$elements) { + my $root_element = $self->get_root_parent($element); + my $category_name = $root_element->cat_name(); + $categorized_elements = $categories->{ $category_name }; + for my $used_element (@$categorized_elements) { + $used_elements->{ $used_element->{'element_id'} } = 1; + } + if (!$used_elements->{$root_element->{'element_id'}}) { + push (@$categorized_elements, $root_element); + $categories->{ $category_name } = $categorized_elements; + } + } + + foreach my $category_name (keys %$categories) { + $xml .= ""; + + $elements = $categories->{$category_name}; + + + foreach my $element (@$elements) { + $element->get_child_elements(); + $xml .= $self->export_element_and_children(1, $element, $environment->{'environment_id'}); + } + + $xml .= ""; + } + + return "$xml"; +} + + +=head2 get_root_parent + +=head2 Description + +Helper sub that returns the root parent element in the environment of the passed in element + +=cut + +sub get_root_parent { + my $self = shift; + my ($element) = @_; + my $parent = $element->get_parent(); + if ($parent->type eq 'env_category') { + return $element; + } + else { + $self->get_root_parent($parent); + } +} + + +=head2 export_element_and_children + +=head2 Description + +Exports Elements and their child elements to XML Recursively. + +=cut + +sub export_element_and_children() { + my $self = shift; + my ($depth, $element, $env_id) = @_; + if ($depth > $max_depth) { + return; + } + $depth++; + + my $xml = ""; + + my $properties = $element->{'properties'}; + foreach my $property (@$properties) { + my $value_selected = Bugzilla::Testopia::Environment->get_value_selected( + $env_id, $element->{'element_id'}, $property->{'property_id'}); + if (defined($value_selected)) { + $xml .= + "" . + "$value_selected"; + } + } + + my $children = $element->{'children'}; + foreach my $child_element (@$children) { + $child_element->get_child_elements(); + $xml .= $self->export_element_and_children($depth, $child_element, $env_id); + } + $xml .= ""; + return $xml; +} + +1; diff --git a/Bugzilla/Testopia/Product.pm b/Bugzilla/Testopia/Product.pm new file mode 100644 index 0000000..f2b91e5 --- /dev/null +++ b/Bugzilla/Testopia/Product.pm @@ -0,0 +1,480 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::Product; + +use strict; + +# Extends Bugzilla::Product; +use base "Bugzilla::Product"; + +use Bugzilla; + +sub environments { + my $self = shift; + my($active, $current) = @_; + my $dbh = Bugzilla->dbh; + + return $self->{'environments'} if defined $self->{'environments'}; + + require Bugzilla::Testopia::Environment; + + my $query = "SELECT environment_id"; + $query .= " FROM test_environments"; + $query .= " WHERE product_id = ?"; + $query .= " AND isactive = 1 OR environment_id = ?" if $active; + + my $ref; + if ($active && $current){ + $ref = $dbh->selectcol_arrayref($query, undef, ($self->{'id'}, $current)); + } + else{ + $ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'}); + } + + my @objs; + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::Environment->new($id); + } + + $self->{'environments'} = \@objs; + return $self->{'environments'}; +} + +sub builds { + my $self = shift; + my($active, $current) = @_; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::Build; + + my $query = "SELECT build_id FROM test_builds WHERE product_id = ?"; + if ($active && $current){ + $query .= " AND isactive = 1 OR build_id = ?"; + } + elsif ($active){ + $query .= " AND isactive = 1"; + } + $query .= " ORDER BY name"; + + my $ref; + if ($active && $current){ + $ref = $dbh->selectcol_arrayref($query, undef, ($self->{'id'}, $current)); + } + elsif ($active){ + $ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'}); + } + else{ + $ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'}); + } + + my @objs; + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::Build->new($id); + } + + $self->{'builds'} = \@objs; + return $self->{'builds'}; +} + +sub categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT category_id + FROM test_case_categories + WHERE product_id = ? + ORDER BY name", + undef, $self->{'id'}); + my @objs; + require Bugzilla::Testopia::Category; + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::Category->new($id); + } + $self->{'categories'} = \@objs; + return $self->{'categories'}; +} + +sub plans { + my $self = shift; + my $dbh = Bugzilla->dbh; + + return $self->{'plans'} if exists $self->{'plans'}; + + require Bugzilla::Testopia::TestPlan; + + my $ref = $dbh->selectcol_arrayref( + "SELECT plan_id + FROM test_plans + WHERE product_id = ? + ORDER BY name", + undef, $self->{'id'}); + my @objs; + + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::TestPlan->new($id); + } + $self->{'plans'} = \@objs; + return $self->{'plans'}; +} + +sub cases { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'cases'} if exists $self->{'cases'}; + + require Bugzilla::Testopia::TestCase; + + my $caseids = $dbh->selectcol_arrayref( + "SELECT case_id FROM test_case_plans + INNER JOIN test_plans on test_case_plans.plan_id = test_plans.plan_id + WHERE test_plans.product_id = ?", + undef, $self->id); + + my @cases; + foreach my $id (@{$caseids}){ + push @cases, Bugzilla::Testopia::TestCase->new($id); + } + + $self->{'cases'} = \@cases; + return $self->{'cases'}; +} + +sub runs { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'runs'} if exists $self->{'runs'}; + + require Bugzilla::Testopia::TestRun; + + my $runids = $dbh->selectcol_arrayref( + "SELECT run_id FROM test_runs + INNER JOIN test_plans ON test_runs.plan_id = test_plans.plan_id + WHERE test_plans.product_id = ?", + undef, $self->id); + + my @runs; + foreach my $id (@{$runids}){ + push @runs, Bugzilla::Testopia::TestRun->new($id); + } + + $self->{'runs'} = \@runs; + return $self->{'runs'}; +} + +sub environment_categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT env_category_id + FROM test_environment_category + WHERE product_id = ?", + undef, $self->id); + my @objs; + require Bugzilla::Testopia::Environment::Category; + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::Environment::Category->new($id); + } + $self->{'environment_categories'} = \@objs; + return $self->{'environment_categories'}; +} + +sub check_product_by_name { + my $self = shift; + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($used) = $dbh->selectrow_array(qq{ + SELECT id + FROM products + WHERE name = ?},undef,$name); + return $used; +} + +sub versions { + my $self = shift; + my ($byid) = shift; + my $id = $byid ? 'id' : 'value'; + my $dbh = Bugzilla->dbh; + + my $values = $dbh->selectall_arrayref( + "SELECT $id AS id, value AS name + FROM versions + WHERE product_id = ? + ORDER BY value", {'Slice' =>{}}, $self->id); + + $self->{'versions'} = $values; + return $self->{'versions'}; +} + +sub milestones { + my $self = shift; + my ($byid) = shift; + my $id = $byid ? 'id' : 'value'; + my $dbh = Bugzilla->dbh; + + my $values = $dbh->selectall_arrayref( + "SELECT $id AS id, value AS name + FROM milestones + WHERE product_id = ? + ORDER BY value", {'Slice' =>{}}, $self->id); + + $self->{'milestones'} = $values; + return $self->{'milestones'}; +} + +sub tags { + my $self = shift; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::TestTag; + + my $ref = $dbh->selectcol_arrayref( + "(SELECT test_tags.tag_id, test_tags.tag_name AS name + FROM test_tags + INNER JOIN test_case_tags ON test_tags.tag_id = test_case_tags.tag_id + INNER JOIN test_cases on test_cases.case_id = test_case_tags.case_id + INNER JOIN test_case_plans on test_case_plans.case_id = test_cases.case_id + INNER JOIN test_plans ON test_plans.plan_id = test_case_plans.plan_id + WHERE test_plans.product_id = ?) + UNION + (SELECT test_tags.tag_id, test_tags.tag_name AS name + FROM test_tags + INNER JOIN test_plan_tags ON test_plan_tags.tag_id = test_tags.tag_id + INNER JOIN test_plans ON test_plan_tags.plan_id = test_plans.plan_id + WHERE test_plans.product_id = ?) + UNION + (SELECT test_tags.tag_id, test_tags.tag_name AS name + FROM test_tags + INNER JOIN test_run_tags ON test_run_tags.tag_id = test_tags.tag_id + INNER JOIN test_runs ON test_runs.run_id = test_run_tags.run_id + INNER JOIN test_plans ON test_plans.plan_id = test_runs.plan_id + WHERE test_plans.product_id = ?) + ORDER BY name", undef, ($self->id,$self->id,$self->id)); + + my @product_tags; + foreach my $id (@$ref){ + push @product_tags, Bugzilla::Testopia::TestTag->new($id); + } + + $self->{'tags'} = \@product_tags; + return $self->{'tags'}; +} + +sub type { + my $self = shift; + $self->{'type'} = 'product'; + return $self->{'type'}; +} + +sub canview { + my $self = shift; + my ($user) = @_; + $user ||= Bugzilla->user; + return 1 if $user->can_see_product($self->name); + return 0; +} + +sub canedit { + my $self = shift; + my ($user) = @_; + $user ||= Bugzilla->user; + return 1 if $user->in_group('Testers') && $user->can_see_product($self->name); + return 0; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $field =~ s/product\.//; + $obj->{$field} = $self->{$field}; + } + + # Add the calculated fields + $obj->{'type'} = $self->type; + $obj->{'id'} = $self->id; + $obj->{'canedit'} = $self->canedit; + + return $json->encode($obj); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Product + +=head1 EXTENDS + +Bugzilla::Product + +=head1 DESCRIPTION + +Provides additional methods and functionality to Bugzilla products +for Testopia specific usage. Methods are read only. For updating and +creating new products, see Bugzilla::Product. + +=head1 SYNOPSIS + +=head2 Creating + + $build = Bugzilla::Testopia::Product->new($product_id); + $build = Bugzilla::Testopia::Product->new({name => $name}); + +=head1 METHODS + +=over + +=item C + + Description: Get the list of builds associated with this product. + + Params: $active - Boolean (optional): True to only include builds with isactive set. + Defaults to False. + $current - Integer (optional): Must be used in conjuntion with $active. + If $active is true then $current should be the current build + selected to prevent it from being excluded if isactive is false. + + Returns: Array: Returns an array of Build objects. + +=item C + + Description: Get the list of cases associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of TestCase objects. + +=item C + + Description: Get the list of categories associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Case Category objects. + +=item C + + Description: Get the list of components associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Component objects. + +=item C + + Description: Get the list of environments associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Environment objects. + +=item C + + Description: Get the list of milestones associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Milestone objects. + +=item C + + Description: Get the list of plans associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Test Plan objects. + +=item C + + Description: Get the list of runs associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Test Run objects. + +=item C + + Description: Get the list of tags associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Tags objects. + +=item C + + Description: Get the list of versions associated with this product. + + Params: $product - Integer/String/Object + Integer: product_id of the product in the Database + String: Product name + Object: Blessed Bugzilla::Product object + + Returns: Array: Returns an array of Version objects. + +=item C B Use Product::get instead + +=item C B Use Product::check_product instead + +=back + +=head1 SEE ALSO + +=over + +L + +L + +=back + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/Testopia/Report.pm b/Bugzilla/Testopia/Report.pm new file mode 100644 index 0000000..0650aab --- /dev/null +++ b/Bugzilla/Testopia/Report.pm @@ -0,0 +1,368 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Greg Hendricks. All Rights Reserved. +# +# Large portions lifted from bugzilla's report.cgi written by +# Gervase Markham +# +# Contributor(s): Greg Hendricks + +=head1 NAME + +Bugzilla::Testopia::Report - Generates report data. + +=head1 DESCRIPTION + +Reports + +=over + +=back + +=head1 SYNOPSIS + + +=cut + +package Bugzilla::Testopia::Report; + +use strict; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; + + +############################### +#### Initialization #### +############################### + +=head1 METHODS + +=head2 new + +Instantiates a new report object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + + my $self = {}; + bless($self, $class); + + $self->init(@_); + + return $self; +} + +=head2 init + +Private constructor for this class + +=cut + +sub init { + my $self = shift; + my ($type, $url, $cgi) = @_; + $self->{'type'} = $type || ThrowCodeError('bad_arg', + {argument => 'type', + function => 'Testopia::Table::_init'}); + $self->{'url_loc'} = $url; + $self->{'cgi'} = $cgi; + my $debug = $cgi->param('debug') if $cgi; + + my $col_field = $cgi->param('x_axis_field') || ''; + my $row_field = $cgi->param('y_axis_field') || ''; + my $tbl_field = $cgi->param('z_axis_field') || ''; + + if (!($col_field || $row_field || $tbl_field)) { + ThrowUserError("no_axes_defined"); + } + + my $width = $cgi->param('width'); + my $height = $cgi->param('height'); + + if (defined($width)) { + (detaint_natural($width) && $width > 0) + || ThrowCodeError("invalid_dimensions"); + $width <= 2000 || ThrowUserError("chart_too_large"); + } + + if (defined($height)) { + (detaint_natural($height) && $height > 0) + || ThrowCodeError("invalid_dimensions"); + $height <= 2000 || ThrowUserError("chart_too_large"); + } + + # These shenanigans are necessary to make sure that both vertical and + # horizontal 1D tables convert to the correct dimension when you ask to + # display them as some sort of chart. + if (defined $cgi->param('format') && $cgi->param('format') eq "table") { + if ($col_field && !$row_field) { + # 1D *tables* should be displayed vertically (with a row_field only) + $row_field = $col_field; + $col_field = ''; + } + } + else { + if ($row_field && !$col_field) { + # 1D *charts* should be displayed horizontally (with an col_field only) + $col_field = $row_field; + $row_field = ''; + } + } + + my %columns; + if ($type eq 'case'){ + $columns{'case_status'} = "map_case_status.name"; + $columns{'priority'} = "map_priority.value"; + $columns{'product'} = "map_case_product.name"; + $columns{'component'} = "map_case_components.name"; + $columns{'category'} = "map_categories.name"; + $columns{'isautomated'} = "test_cases.isautomated"; + $columns{'tags'} = "map_case_tags.tag_name"; + $columns{'requirement'} = "test_cases.requirement"; + $columns{'author'} = "map_case_author.login_name"; + $columns{'default_tester'} = "map_default_tester.login_name"; + } + elsif ($type eq 'run'){ + $columns{'run_status'} = "test_runs.stop_date"; + $columns{'product'} = "map_run_product.name"; + $columns{'build'} = "map_run_build.name"; + $columns{'milestone'} = "map_run_milestone.milestone"; + $columns{'environment'} = "map_run_environment.name"; + $columns{'tags'} = "map_run_tags.tag_name"; + $columns{'manager'} = "map_run_manager.login_name"; + $columns{'default_product_version'} = "test_runs.product_version"; + } + elsif ($type eq 'plan'){ + $columns{'plan_type'} = "map_plan_type.name"; + $columns{'product'} = "map_plan_product.name"; + $columns{'archived'} = "test_plans.isactive"; + $columns{'tags'} = "map_plan_tags.tag_name"; + $columns{'author'} = "map_plan_author.login_name"; + $columns{'default_product_version'} = "test_plans.default_product_version"; + } + elsif ($type eq 'caserun'){ + $columns{'build'} = "map_caserun_build.name"; + $columns{'case'} = "map_caserun_case.summary"; + $columns{'run'} = "map_caserun_run.summary"; + $columns{'environment'} = "map_caserun_environment.name"; + $columns{'assignee'} = "map_caserun_assignee.login_name"; + $columns{'testedby'} = "map_caserun_testedby.login_name"; + $columns{'case_run_status'} = "map_caserun_status.name"; + $columns{'milestone'} = "map_caserun_milestone.milestone"; + $columns{'case_tags'} = "map_caserun_case_tags.tag_name"; + $columns{'run_tags'} = "map_caserun_run_tags.tag_name"; + $columns{'requirement'} = "map_caserun_cases.requirement"; + $columns{'priority'} = "map_caserun_priority.value"; + $columns{'default_tester'} = "map_caserun_default_tester.login_name"; + $columns{'category'} = "map_caserun_category.name"; + $columns{'component'} = "map_caserun_components.name"; + } + # One which means "nothing". Any number would do, really. It just gets SELECTed + # so that we always select 3 items in the query. + $columns{''} = "42217354"; + + # Validate the values in the axis fields or throw an error. + !$row_field + || ($columns{$row_field} && trick_taint($row_field)) + || ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field}); + !$col_field + || ($columns{$col_field} && trick_taint($col_field)) + || ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field}); + !$tbl_field + || ($columns{$tbl_field} && trick_taint($tbl_field)) + || ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field}); + + my @axis_fields = ($row_field, $col_field, $tbl_field); + my @selectnames = map($columns{$_}, @axis_fields); + $self->{'axis_fields'} = \@axis_fields; + $self->{'selectnames'} = \@selectnames; + $cgi->param('viewall', 1); + + my $dbh = Bugzilla->switch_to_shadow_db; + my $search = Bugzilla::Testopia::Search->new($cgi, \@selectnames); + my $results = $dbh->selectall_arrayref($search->query); + $dbh = Bugzilla->switch_to_main_db; + + # We have a hash of hashes for the data itself, and a hash to hold the + # row/col/table names. + my %data; + my %names; + + # Read the bug data and count the bugs for each possible value of row, column + # and table. + # + # We detect a numerical field, and sort appropriately, if all the values are + # numeric. + my $col_isnumeric = 1; + my $row_isnumeric = 1; + my $tbl_isnumeric = 1; + + foreach my $result (@$results) { + my ($row, $col, $tbl) = @$result; + + # handle empty dimension member names + $row = ' ' if ($row eq ''); + $col = ' ' if ($col eq ''); + $tbl = ' ' if ($tbl eq ''); + + $row = "" if ($row eq $columns{''}); + $col = "" if ($col eq $columns{''}); + $tbl = "" if ($tbl eq $columns{''}); + + # account for the fact that names may start with '_' or '.'. Change this + # so the template doesn't hide hash elements with those keys + $row =~ s/^([._])/ $1/; + $col =~ s/^([._])/ $1/; + $tbl =~ s/^([._])/ $1/; + + $data{$tbl}{$col}{$row}++; + $names{"col"}{$col}++; + $names{"row"}{$row}++; + $names{"tbl"}{$tbl}++; + + $col_isnumeric &&= ($col =~ /^-?\d+(\.\d+)?$/o); + $row_isnumeric &&= ($row =~ /^-?\d+(\.\d+)?$/o); + $tbl_isnumeric &&= ($tbl =~ /^-?\d+(\.\d+)?$/o); + } + + my @col_names = @{get_names($names{"col"}, $col_isnumeric, $col_field)}; + my @row_names = @{get_names($names{"row"}, $row_isnumeric, $row_field)}; + my @tbl_names = @{get_names($names{"tbl"}, $tbl_isnumeric, $tbl_field)}; + + # The GD::Graph package requires a particular format of data, so once we've + # gathered everything into the hashes and made sure we know the size of the + # data, we reformat it into an array of arrays of arrays of data. + push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1); + + my @image_data; + foreach my $tbl (@tbl_names) { + my @tbl_data; + push(@tbl_data, \@col_names); + foreach my $row (@row_names) { + my @col_data; + foreach my $col (@col_names) { + $data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0; + push(@col_data, $data{$tbl}{$col}{$row}); + if ($tbl ne "-total-") { + # This is a bit sneaky. We spend every loop except the last + # building up the -total- data, and then last time round, + # we process it as another tbl, and push() the total values + # into the image_data array. + $data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row}; + } + } + + push(@tbl_data, \@col_data); + } + + unshift(@image_data, \@tbl_data); + } + $self->{'col_field'} = $col_field; + $self->{'row_field'} = $row_field; + $self->{'tbl_field'} = $tbl_field; + + my @time = localtime(time()); + my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; + $self->{'date'} = $date; + $self->{'format'} = $cgi->param('format'); + + $self->{'col_names'} = \@col_names; + $self->{'row_names'} = \@row_names; + $self->{'tbl_names'} = \@tbl_names; + + # Below a certain width, we don't see any bars, so there needs to be a minimum. + if ($width && $cgi->param('format') eq "bar") { + my $min_width = (scalar(@col_names) || 1) * 20; + + if (!$cgi->param('cumulate')) { + $min_width *= (scalar(@row_names) || 1); + } + + $self->{'min_width'} = $min_width; + } + + $self->{'width'} = $width if $width; + $self->{'height'} = $height if $height; + + $self->{'query'} = $search->query; + $self->{'debug'} = $cgi->param('debug'); + + $self->{'data'} = \%data; + $self->{'image_data'} = \@image_data; + $self->{'report_loc'} = "tr_" . $type . "_reports.cgi"; + if ($cgi->param('debug')) { + print $cgi->header; + require Data::Dumper; + print "
data hash:\n";
+        print Data::Dumper::Dumper(%data) . "\n\n";
+        print "data array:\n";
+        print Data::Dumper::Dumper(@image_data) . "\n\n
"; + } + + return $self; +} + +sub get_names { + my ($names, $isnumeric, $field) = @_; + + my @sorted; + + if ($isnumeric) { + # It's not a field we are preserving the order of, so sort it + # numerically... + sub numerically { $a <=> $b } + @sorted = sort numerically keys(%{$names}); + } else { + # ...or alphabetically, as appropriate. + @sorted = sort(keys(%{$names})); + } + + return \@sorted; +} +sub listbase{ + my $self = shift; + my $cgi = $self->{'cgi'}; + $self->{'listbase'} = $cgi->canonicalise_query( + "x_axis_field", "y_axis_field", "z_axis_field", + "ctype", "format", "query_format", "report_action", @{$self->{'axis_fields'}}); + return $self->{'listbase'}; +} + +sub imagebase { + my $self = shift; + my $cgi = $self->{'cgi'}; + $self->{'imagebase'} = $cgi->canonicalise_query( + $self->{'tbl_field'}, "report_action", "ctype", "format", "width", "height"); + return $self->{'imagebase'}; +} + +sub switchbase { + my $self = shift; + my $cgi = $self->{'cgi'}; + $self->{'switchbase'} = $cgi->canonicalise_query( + "query_format", "report_action", "ctype", "format", "width", "height"); + return $self->{'switchbase'}; +} + +1; diff --git a/Bugzilla/Testopia/Search.pm b/Bugzilla/Testopia/Search.pm new file mode 100644 index 0000000..d2e8ac2 --- /dev/null +++ b/Bugzilla/Testopia/Search.pm @@ -0,0 +1,1841 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. + +# Large portions lifted uncerimoniously from Bugzilla::Search.pm +# Which is copyrighted by its respective copyright holders +# Many thanks to the geniouses that contributed to that work of art: +# Gervase Markham +# Terry Weissman +# Dan Mosedale +# Stephan Niemz +# Andreas Franke +# Myk Melez +# Michael Schindler +# Max Kanat-Alexander +# +# Contributor(s): Greg Hendricks + +=head1 NAME + +Bugzilla::Testopia::Search - A module to support searches in Testopis + +=head1 DESCRIPTION + +Testopia::Search is based heavilly on Bugzilla::Search. It takes a +CGI instance and parses its parameters to generate an SQL query that +can be used to get a result set from the database. The query is +usually passed to Table.pm to execute and display the results. +Search.pm supports searching for all major objects in the Testopia +database, Cases, Plans, Runs and Case-runs. It supports sorting +on one column at a time in ascending order. + +=head1 SYNOPSIS + + $search = Bugzilla::Testopia::Search($cgi); + +=cut + +package Bugzilla::Testopia::Search; + +use strict; + +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestCase; + +use Date::Format; +use Date::Parse; +use Data::Dumper; + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + + my $self = {}; + bless($self, $class); + + $self->init(@_); + + return $self; +} + +sub init { + my $self = shift; + my $cgi = shift; + my $fields = shift; + my $user = $self->{'user'} || Bugzilla->user; + $self->{'cgi'} = $cgi; + $self->{'fields'} = $fields if $fields; + my $debug = $cgi->param('debug') || 0; + my $dbh = Bugzilla->dbh; + print $cgi->header if $debug; + if ($debug && !$cgi->{'final_separator'}){ + print "

URL Params

"; + print '

', join('
', split('&', $cgi->canonicalise_query)), "

"; + } + my $page = $cgi->param('page') || 0; + my $start = $cgi->param('start') || 0; + my $limit = $cgi->param('limit') || 25; + detaint_natural($page) if $page; + detaint_natural($start) if $start; + detaint_natural($limit) if $limit; + if ($cgi->param('viewall')){ + $page = undef; + $start = undef; + } + my $pagesize; + if ($cgi->param('pagesize') || $cgi->param('page')){ + $pagesize = $cgi->param('pagesize'); + $start = undef; + } + detaint_natural($pagesize) if defined $pagesize; + $pagesize ||= 25; + my $sortdir; + if ($cgi->param('dir')){ + $sortdir = $cgi->param('dir') eq 'ASC' ? 'ASC' : 'DESC'; + } + # What a Hack! + # For the missing cases report, this is the simplest query that can return the list + # Just set it and forget the rest. + if ($cgi->param('report_type') && $cgi->param('report_type') eq 'missing'){ + my @plan_ids = split(',',$cgi->param('plan_ids')); + my @plans; + foreach my $p (@plan_ids){ + my $plan = Bugzilla::Testopia::TestPlan->new(trim($p)); + push @plans, $plan->id if $plan && $plan->canview; + } + my $plans = join(',',@plans); + my $query = "SELECT case_id + FROM test_case_plans + WHERE case_id NOT IN (SELECT case_id FROM test_case_runs) + AND plan_id IN ($plans)"; + $self->{'sql'} = $query; + return; + } + if ($cgi->param('report_type') && $cgi->param('report_type') eq 'myplans'){ + my $query = "SELECT plan_id FROM test_case_plans + INNER JOIN test_cases on test_case_plans.case_id = test_cases.case_id + WHERE test_cases.default_tester_id = " . Bugzilla->user->id . + " UNION SELECT plan_id FROM test_plan_permissions WHERE userid = " . Bugzilla->user->id; + $self->{'sql'} = $query; + return; + } + + my $distinct = $cgi->param('distinct') ? 'DISTINCT' : ''; + + my @specialchart; + my @supptables; + my @wherepart; + my @having; + my @groupby; + my @andlist; + my @orderby; + my @inputorder; + my @fields; + my %specialorderjoin; + my %chartfields; + #my ($testergroup) = $dbh->selectrow_array("SELECT id FROM groups WHERE name = ?",undef, 'Testers'); + +# $chartid is the number of the current chart whose SQL we're constructing +# $row is the current row of the current chart + +# names for table aliases are constructed using $chartid and $row +# SELECT blah FROM $table "$table_$chartid_$row" WHERE .... + +# $f = field of table in bug db (e.g. bug_id, reporter, etc) +# $ff = qualified field name (field name prefixed by table) +# e.g. bugs_activity.bug_id +# $t = type of query. e.g. "equal to", "changed after", case sensitive substr" +# $v = value - value the user typed in to the form +# $q = sanitized version of user input (SqlQuote($v)) +# @supptables = Tables and/or table aliases used in query +# %suppseen = A hash used to store all the tables in supptables to weed +# out duplicates. +# @supplist = A list used to accumulate all the JOIN clauses for each +# chart to merge the ON sections of each. +# $suppstring = String which is pasted into query containing all table names + my $chartid; + my $sequence = 0; + my $f; + my $ff; + my $t; + my $q; + my $v; + my $term; + my %funcsbykey; + my $type; + + my $obj = trim($cgi->param('current_tab')) || ThrowUserError('testopia-missing-parameter', {'param' => 'current_tab'}); + ThrowUserError('testopia-unknown-tab', {tab => $obj}) if $obj !~ '^(case|plan|run|case_run|environment)$'; + trick_taint($obj); + + # If what we intend to do is generate a report, we need some tables + # to map names to ids + if ($fields){ + ## Cases ## + if (grep(/map_categories/, @$fields)) { + push @supptables, "INNER JOIN test_case_categories AS map_categories " . + "ON test_cases.category_id = map_categories.category_id"; + } + if (grep(/map_priority/, @$fields)) { + push @supptables, "INNER JOIN priority AS map_priority " . + "ON test_cases.priority_id = map_priority.id"; + } + if (grep(/map_case_status/, @$fields)) { + push @supptables, "INNER JOIN test_case_status AS map_case_status " . + "ON test_cases.case_status_id = map_case_status.case_status_id"; + } + if (grep(/map_case_components/, @$fields)) { + push @supptables, "INNER JOIN test_case_components AS tccomps " . + "ON test_cases.case_id = tccomps.case_id"; + push @supptables, "INNER JOIN components AS map_case_components " . + "ON tccomps.component_id = map_case_components.id"; + } + if (grep(/map_case_product/, @$fields)) { + push(@supptables, "INNER JOIN test_case_plans AS map_case_plans " . + "ON test_cases.case_id = map_case_plans.case_id"); + push(@supptables, "INNER JOIN test_plans AS map_product_plans " . + "ON map_case_plans.plan_id = map_product_plans.plan_id"); + push(@supptables, "INNER JOIN products AS map_case_product " . + "ON map_product_plans.product_id = map_case_product.id"); + } + if (grep(/map_case_tags/, @$fields)) { + push @supptables, "INNER JOIN test_case_tags AS tctags " . + "ON test_cases.case_id = tctags.case_id"; + push @supptables, "INNER JOIN test_tags AS map_case_tags " . + "ON tctags.tag_id = map_case_tags.tag_id"; + } + if (grep(/map_case_author/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_case_author " . + "ON test_cases.author_id = map_case_author.userid"; + } + if (grep(/map_default_tester/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_default_tester " . + "ON test_cases.default_tester_id = map_default_tester.userid"; + } + ## Runs ## + + if (grep(/map_run_product/, @$fields)) { + push @supptables, "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"; + push @supptables, "INNER JOIN products AS map_run_product " . + "ON test_plans.product_id = map_run_product.id"; + } + if (grep(/map_run_build/, @$fields)) { + push @supptables, "INNER JOIN test_builds AS map_run_build " . + "ON test_runs.build_id = map_run_build.build_id"; + } + if (grep(/map_run_milestone/, @$fields)) { + push @supptables, "INNER JOIN test_builds AS map_run_milestone " . + "ON test_runs.build_id = map_run_milestone.build_id"; + } + if (grep(/map_run_environment/, @$fields)) { + push @supptables, "INNER JOIN test_environments AS map_run_environment " . + "ON test_runs.environment_id = map_run_environment.environment_id"; + } + if (grep(/map_run_tags/, @$fields)) { + push @supptables, "INNER JOIN test_run_tags " . + "ON test_runs.run_id = test_run_tags.run_id"; + push @supptables, "INNER JOIN test_tags AS map_run_tags " . + "ON test_run_tags.tag_id = map_run_tags.tag_id"; + } + if (grep(/map_run_manager/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_run_manager " . + "ON test_runs.manager_id = map_run_manager.userid"; + } + + ## Plans ## + if (grep(/map_plan_type/, @$fields)) { + push @supptables, "INNER JOIN test_plan_types AS map_plan_type " . + "ON test_plans.type_id = map_plan_type.type_id"; + } + if (grep(/map_plan_product/, @$fields)) { + push @supptables, "INNER JOIN products AS map_plan_product " . + "ON test_plans.product_id = map_plan_product.id"; + } + if (grep(/map_plan_tags/, @$fields)) { + push @supptables, "INNER JOIN test_plan_tags " . + "ON test_plans.plan_id = test_plan_tags.plan_id"; + push @supptables, "INNER JOIN test_tags AS map_plan_tags " . + "ON test_plan_tags.tag_id = map_plan_tags.tag_id"; + } + if (grep(/map_plan_author/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_plan_author " . + "ON test_plans.author_id = map_plan_author.userid"; + } + ## Case-runs ## + if (grep(/map_caserun_assignee/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_caserun_assignee " . + "ON test_case_runs.assignee = map_caserun_assignee.userid"; + } + if (grep(/map_caserun_testedby/, @$fields)) { + push @supptables, "INNER JOIN profiles AS map_caserun_testedby " . + "ON test_case_runs.testedby = map_caserun_testedby.userid"; + } + if (grep(/map_caserun_build/, @$fields)) { + push @supptables, "INNER JOIN test_builds AS map_caserun_build " . + "ON test_case_runs.build_id = map_caserun_build.build_id"; + } + if (grep(/map_caserun_environment/, @$fields)) { + push @supptables, "INNER JOIN test_environments AS map_caserun_environment " . + "ON test_case_runs.environment_id = map_caserun_environment.environment_id"; + } + if (grep(/map_caserun_status/, @$fields)) { + push @supptables, "INNER JOIN test_case_run_status AS map_caserun_status " . + "ON test_case_runs.case_run_status_id = map_caserun_status.case_run_status_id"; + } + if (grep(/map_caserun_milestone/, @$fields)) { + push @supptables, "INNER JOIN test_builds AS map_caserun_milestone " . + "ON test_case_runs.build_id = map_caserun_milestone.build_id"; + } + if (grep(/map_caserun_case_tags/, @$fields)) { + push @supptables, "INNER JOIN test_case_tags AS tctags " . + "ON test_case_runs.case_id = tctags.case_id"; + push @supptables, "INNER JOIN test_tags AS map_caserun_case_tags " . + "ON tctags.tag_id = map_caserun_case_tags.tag_id"; + } + if (grep(/map_caserun_run_tags/, @$fields)) { + push @supptables, "INNER JOIN test_run_tags " . + "ON test_case_runs.run_id = test_run_tags.run_id"; + push @supptables, "INNER JOIN test_tags AS map_caserun_run_tags " . + "ON test_run_tags.tag_id = map_caserun_run_tags.tag_id"; + } + if (grep(/map_caserun_cases/, @$fields)) { + push @supptables, "INNER JOIN test_cases AS map_caserun_cases " . + "ON test_case_runs.case_id = map_caserun_cases.case_id"; + } + if (grep(/map_caserun_priority/, @$fields)) { + push @supptables, "INNER JOIN test_cases AS map_caserun_cases " . + "ON test_case_runs.case_id = map_caserun_cases.case_id"; + push @supptables, "INNER JOIN priority AS map_caserun_priority " . + "ON map_caserun_cases.priority_id = map_caserun_priority.id"; + } + if (grep(/map_caserun_default_tester/, @$fields)) { + push @supptables, "INNER JOIN test_cases AS map_caserun_cases " . + "ON test_case_runs.case_id = map_caserun_cases.case_id"; + push @supptables, "INNER JOIN profiles AS map_caserun_default_tester " . + "ON map_caserun_cases.default_tester_id = map_caserun_default_tester.userid"; + } + if (grep(/map_caserun_category/, @$fields)) { + push @supptables, "INNER JOIN test_cases AS map_caserun_cases " . + "ON test_case_runs.case_id = map_caserun_cases.case_id"; + push @supptables, "INNER JOIN test_case_categories AS map_caserun_category " . + "ON map_caserun_cases.category_id = map_caserun_category.category_id"; + } + if (grep(/map_caserun_components/, @$fields)) { + push @supptables, "INNER JOIN test_cases AS map_caserun_cases " . + "ON test_case_runs.case_id = map_caserun_cases.case_id"; + push @supptables, "INNER JOIN test_case_components AS case_components " . + "ON map_caserun_cases.case_id = case_components.case_id"; + push @supptables, "INNER JOIN components AS map_caserun_components " . + "ON case_components.component_id = map_caserun_components.id"; + } + + } + # Set up tables for access control + unless (Bugzilla->user->in_group('Testers')){ + if ($obj eq 'case'){ + push(@supptables, "INNER JOIN test_case_plans AS case_plans " . + "ON test_cases.case_id = case_plans.case_id"); + push(@supptables, "INNER JOIN test_plans " . + "ON case_plans.plan_id = test_plans.plan_id"); } + elsif ($obj eq 'case_run'){ + push(@supptables, "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + push(@supptables, "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + } + elsif ($obj eq 'run'){ + push(@supptables, "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + } + unless ($obj eq 'environment'){ + push @supptables, "INNER JOIN test_plan_permissions ON test_plans.plan_id = test_plan_permissions.plan_id"; + push @wherepart, "test_plan_permissions.permissions > 0 AND test_plan_permissions.userid = ". Bugzilla->user->id; + } + } + # Set up tables for field sort order + my $order = $cgi->param('order') || ''; + if ($order eq 'author') { + push @supptables, "INNER JOIN profiles as map_author ON map_author.userid = test_". $obj ."s.author_id"; + push @orderby, 'map_author.login_name'; + } + elsif ($order eq 'manager') { + push @supptables, "INNER JOIN profiles as map_manager ON map_manager.userid = test_". $obj ."s.manager_id"; + push @orderby, 'map_manager.login_name'; + } + elsif ($order eq 'assignee') { + if ($obj eq 'case_run'){ + push @supptables, "LEFT JOIN profiles as map_assignee_order ON map_assignee_order.userid = test_". $obj ."s.assignee"; + push @orderby, 'map_assignee_order.login_name'; + } + } + elsif ($order eq 'testedby') { + push @supptables, "LEFT JOIN profiles as map_testedby_order ON map_testedby_order.userid = test_". $obj ."s.testedby"; + push @orderby, 'map_testedby_order.login_name'; + } + elsif ($order eq 'tester') { + push @supptables, "LEFT JOIN profiles as map_tester ON map_tester.userid = test_". $obj ."s.default_tester_id"; + push @orderby, 'map_tester.login_name'; + } + elsif ($order eq 'product') { + push @supptables, "LEFT JOIN products ON products.id = test_". $obj ."s.product_id"; + push @orderby, 'products.name'; + } + elsif ($order eq 'build') { + push @supptables, "INNER JOIN test_builds AS build ON build.build_id = test_". $obj ."s.build_id"; + push @orderby, 'build.name'; + } + elsif ($order eq 'environment') { + push @supptables, "INNER JOIN test_environments AS env ON env.environment_id = test_". $obj ."s.environment_id"; + push @orderby, 'env.name'; + } + elsif ($order eq 'plan_type') { + push @supptables, "INNER JOIN test_plan_types AS ptype ON ptype.type_id = test_plans.type_id"; + push @orderby, 'ptype.name'; + } + elsif ($order eq 'plan_prodver') { + push @supptables, "INNER JOIN versions ON versions.value = test_plans.default_product_version"; + push @orderby, 'versions.value'; + } + elsif($order eq 'plan_id' && $obj eq 'case_run'){ + push @supptables, "INNER JOIN test_case_plans AS case_plans ON test_cases.case_id = case_plans.case_id"; + push @orderby, 'case_plans.plan_id'; + } + elsif($order eq 'requirement' && $obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; + push @orderby, 'test_cases.requirement'; + } + elsif ($order eq 'priority') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; + } + push @supptables, "INNER JOIN priority ON priority.id = test_cases.priority_id"; + push @orderby, 'test_cases.priority_id'; + } + elsif ($order eq 'build') { + push @supptables, "INNER JOIN test_builds ON test_builds.build_id = test_case_runs.build_id"; + push @orderby, 'test_builds.name'; + } + elsif ($order eq 'status') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_case_run_status as case_run_status ON case_run_status.case_run_status_id = test_case_runs.case_run_status_id"; + push @orderby, 'case_run_status.sortkey'; + } + elsif ($obj eq 'case'){ + push @supptables, "INNER JOIN test_case_status AS case_status ON test_cases.case_status_id = case_status.case_status_id"; + push @orderby, 'case_status.name'; + } + elsif ($obj eq 'run'){ + push @orderby, 'test_runs.stop_date'; + } + } + elsif ($order eq 'category') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; + } + push @supptables, "INNER JOIN test_case_categories AS categories ON test_cases.category_id = categories.category_id"; + push @orderby, 'categories.name'; + } + elsif ($order eq 'component') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; + } + push @supptables, "INNER JOIN test_case_components ON test_cases.case_id = test_case_components.case_id"; + push @supptables, "LEFT JOIN components AS comp_sort ON comp_sort.id = test_case_components.component_id"; + push @orderby, 'comp_sort.name'; + } + elsif ($order eq 'summary') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases AS cases ON cases.case_id = test_case_runs.case_id"; + push @orderby, 'cases.summary'; + } + else{ + push @orderby, 'test_'. $obj .'s.summary'; + } + } + elsif ($order eq 'created') { + push @orderby, 'test_'. $obj .'s.creation_date'; + } + elsif ($order eq 'name') { + push @orderby, 'test_'. $obj .'s.name'; + } + else{ + if ($order){ + trick_taint($order) if ($order); + push @orderby, 'test_'. $obj .'s.' . $order; + } + } + + my @funcdefs = + ( + "^category," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases " . + "ON test_case_runs.case_id = test_cases.case_id"); + } + push(@supptables, + "INNER JOIN test_case_categories AS categories " . + "ON test_cases.category_id = categories.category_id"); + $f = "categories.name"; + }, + "^category_id," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases " . + "ON test_case_runs.case_id = test_cases.case_id"); + } + + $f = "test_cases.category_id"; + }, + "^build," => sub { + my $str = ''; + $str = 'case_' if ($obj eq 'case_run'); + push(@supptables, + "INNER JOIN test_builds AS builds " . + "ON test_". $str ."runs.build_id = builds.build_id"); + $f = "builds.name"; + }, + "^(tcaction|tceffect)," => sub { + push(@supptables, + "LEFT JOIN test_case_texts AS case_texts " . + "ON test_cases.case_id = case_texts.case_id"); + my $fid = ($1 eq 'tcaction' ? 'action' : 'effect'); + $f = "case_texts.$fid"; + }, + "^plan_text," => sub { + push(@supptables, + "LEFT JOIN test_plan_texts AS plan_texts " . + "ON test_plans.plan_id = plan_texts.plan_id"); + $f = "plan_texts.plan_text"; + }, + "^prod_name," => sub { + push(@supptables, + "INNER JOIN products ". + "ON test_". $obj ."s.product_id = products.id"); + $f = 'products.name'; + }, + "^case_status," => sub { + push(@supptables, + "INNER JOIN test_case_status AS case_status " . + "ON test_cases.case_status_id = case_status.case_status_id"); + $f = 'case_status.name'; + }, + "^priority," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + push(@supptables, + "INNER JOIN priority ". + "ON test_cases.priority_id = priority.id"); + $f = 'priority.value'; + }, + "^environment," => sub { + push(@supptables, + "INNER JOIN test_environments ". + "ON test_". $obj ."s.environment_id = test_environments.environment_id"); + $f = 'test_environments.name'; + }, + "^environment_name," => sub { + push(@supptables, + "INNER JOIN test_environments ". + "ON test_". $obj ."s.environment_id = test_environments.environment_id"); + $f = 'test_environments.name'; + }, + "^plan_type," => sub { + push(@supptables, + "INNER JOIN test_plan_types ". + "ON test_plans.type_id = test_plan_types.type_id"); + $f = 'test_plan_types.name'; + }, + "^plan_perms," => sub { + push(@supptables, + "INNER JOIN test_plan_permissions ". + "ON test_plans.plan_id = test_plan_permissions.plan_id"); + if ($cgi->param('case_plans_tester')){ + $f = 'test_plan_permissions.userid'; + } + else { + $f = 'test_plan_permissions.permissions'; + } + }, + "^case_run_status," => sub { + if ($obj eq 'case'){ + push(@supptables, + "INNER JOIN test_case_run_status AS tcrs ". + "ON case_runs.case_run_status_id = tcrs.case_run_status_id"); + } + else { + push(@supptables, + "INNER JOIN test_case_run_status AS tcrs ". + "ON test_case_runs.case_run_status_id = tcrs.case_run_status_id"); + } + $f = 'tcrs.name'; + }, + "^env_products," => sub { + push(@supptables, + "INNER JOIN products as env_products + ON test_environments.product_id = env_products.id"); + $f = 'env_products.id' + }, + "^env_.*," => sub { + if ($obj eq 'run'){ + push(@supptables, + "INNER JOIN test_environment_map + ON test_runs.environment_id = test_environment_map.environment_id"); + } + elsif ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_environment_map + ON test_case_runs.environment_id = test_environment_map.environment_id"); + } + }, + "^env_category," => sub { + push(@supptables, + "INNER JOIN test_environment_element + ON test_environment_map.element_id = test_environment_element.element_id"); + push(@supptables, + "INNER JOIN test_environment_category + ON test_environment_element.env_category_id = test_environment_category.env_category_id"); + $f = 'test_environment_category.name' + }, + "^env_element," => sub { + push(@supptables, + "INNER JOIN test_environment_element as env_element + ON test_environment_map.element_id = env_element.element_id"); + $f = 'env_element.name' + }, + "^env_property," => sub { + push(@supptables, + "INNER JOIN test_environment_property as env_property + ON test_environment_map.property_id = env_property.property_id"); + $f = 'env_property.name' + }, + "^env_value," => sub { + $f = 'test_environment_map.value_selected' + }, + "^env_value_selected," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_value_selected + ON test_environments.environment_id = env_map_value_selected.environment_id"); + $f = 'env_map_value_selected.value_selected' + }, + "^component," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + push(@supptables, + "INNER JOIN test_case_components AS tc_components " . + "ON test_cases.case_id = tc_components.case_id"); + push(@supptables, + "INNER JOIN components ". + "ON components.id = tc_components.component_id"); + $f = "components.name"; + }, + "^priority_id," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + $f = "test_cases.priority_id"; + }, + "^isautomated," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + $f = "test_cases.isautomated"; + }, + "^alias," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + $f = "test_cases.alias"; + }, + "^milestone," => sub { + push(@supptables, + "INNER JOIN test_builds AS builds " . + "ON test_runs.build_id = builds.build_id"); + push(@supptables, + "INNER JOIN milestones ". + "ON milestones.value = builds.milestone"); + $f = "milestones.value"; + }, + "^(?:assigned_to|reporter|qa_contact|bug.*)," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_case_bugs AS case_bugs " . + "ON test_case_runs.case_run_id = case_bugs.case_run_id"); + push(@supptables, + "INNER JOIN bugs ". + "ON case_bugs.bug_id = bugs.bug_id"); + } + elsif($obj eq 'case'){ + push(@supptables, + "INNER JOIN test_case_bugs AS case_bugs " . + "ON test_cases.case_id = case_bugs.case_id"); + push(@supptables, + "INNER JOIN bugs ". + "ON case_bugs.bug_id = bugs.bug_id"); + } + push(@supptables, + "INNER JOIN bugs " . + "ON case_bugs.bug_id = bugs.bug_id"); + }, + "^bug," => sub { + $f = "bugs.bug_id"; + }, + "^bug_status," => sub { + $f = "bugs.bug_status"; + }, + "^bug_resolution," => sub { + $f = "bugs.resolution"; + }, + "^bug_priority," => sub { + $f = "bugs.priority"; + },"^bug_op_sys," => sub { + $f = "bugs.op_sys"; + }, + "^bug_severity," => sub { + $f = "bugs.bug_severity"; + }, + "^bug_rep_platform," => sub { + $f = "bugs.rep_platform"; + }, + "^bug_short_desc," => sub { + $f = "bugs.short_desc"; + }, + "^bug_file_loc," => sub { + $f = "bugs.bug_file_loc"; + }, + "^bug_status_whiteboard," => sub { + $f = "bugs.status_whiteboard"; + }, + "^bug_long_desc," => sub { + my $table = "longdescs"; + my $extra = ""; + if (Bugzilla->params->{"insidergroup"} + && !Bugzilla->user->in_group(Bugzilla->params->{"insidergroup"})) + { + $extra = "AND $table.isprivate < 1"; + } + push(@supptables, "INNER JOIN longdescs AS $table " . + "ON $table.bug_id = bugs.bug_id $extra"); + $f = "$table.thetext"; + }, + "^bug_keywords," => sub { + my @list; + my $table = "keywords"; + foreach my $value (split(/[\s,]+/, $v)) { + if ($value eq '') { + next; + } + my $keyword = new Bugzilla::Keyword({name => $value}); + if ($keyword) { + push(@list, "$table.keywordid = " . $keyword->id); + } + else { + ThrowUserError("unknown_keyword", + { keyword => $v }); + } + } + my $haveawordterm; + if (@list) { + $haveawordterm = "(" . join(' OR ', @list) . ")"; + if ($t eq "anywords") { + $term = $haveawordterm; + } elsif ($t eq "allwords") { + my $ref = $funcsbykey{",$t"}; + &$ref; + if ($term && $haveawordterm) { + $term = "(($term) AND $haveawordterm)"; + } + } + } + if ($term) { + push(@supptables, "LEFT JOIN keywords AS $table " . + "ON $table.bug_id = bugs.bug_id"); + } + $f = "bugs.keywords"; + }, + "^(?:assigned_to|reporter|qa_contact)," => sub { + push(@supptables, "INNER JOIN profiles AS map_$f " . + "ON bugs.$f = map_$f.userid"); + $f = "map_$f.login_name"; + }, + "^case_summary," => sub { + push(@supptables, + "INNER JOIN test_cases AS cases " . + "ON cases.case_id = test_case_runs.case_id"); + $f = "cases.summary"; + }, + + "^tags," => sub { + if ($t !~ 'notag'){ + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases " . + "ON test_case_runs.case_id = test_cases.case_id"); + push(@supptables, + "INNER JOIN test_case_tags AS case_tags " . + "ON test_cases.case_id = case_tags.case_id"); + push(@supptables, + "INNER JOIN test_tags " . + "ON case_tags.tag_id = test_tags.tag_id"); + } + else{ + push(@supptables, + "INNER JOIN test_". $obj ."_tags AS ". $obj ."_tags " . + "ON test_". $obj ."s.". $obj ."_id = ". $obj ."_tags.". $obj ."_id"); + push(@supptables, + "INNER JOIN test_tags " . + "ON ". $obj ."_tags.tag_id = test_tags.tag_id"); + } + } + $f = "test_tags.tag_name"; + }, + "^requirement," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases " . + "ON test_case_runs.case_id = test_cases.case_id"); + } + $f = "test_cases.requirement"; + }, + "^case_plan_id," => sub { + if ($cgi->param('case_plans_tester')){ + my $join = $cgi->param('case_plans_tester') ? 'LEFT' : 'INNER'; + push(@supptables, + "$join JOIN test_case_plans AS case_plans_tester " . + "ON test_plans.plan_id = case_plans_tester.plan_id"); + push(@supptables, + "$join JOIN test_cases " . + "ON case_plans_tester.case_id = test_cases.case_id"); + push(@supptables, + "$join JOIN profiles as map_plan_testers " . + "ON test_cases.default_tester_id = map_plan_testers.userid"); + $f = "map_plan_testers.login_name"; + } + else { + push(@supptables, + "INNER JOIN test_case_plans AS case_plans " . + "ON test_cases.case_id = case_plans.case_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON case_plans.plan_id = test_plans.plan_id"); + $f = "test_plans.plan_id"; + } + }, + "^plan_case_id," => sub { + push(@supptables, + "INNER JOIN test_case_plans AS case_plans " . + "ON test_plans.plan_id = case_plans.plan_id"); + push(@supptables, + "INNER JOIN test_cases " . + "ON case_plans.case_id = test_cases.case_id"); + $f = "test_cases.case_id"; + }, + "^cases_in_runs," => sub { + push(@supptables, + "INNER JOIN test_case_runs AS case_runs " . + "ON test_cases.case_id = case_runs.case_id"); + push(@supptables, + "INNER JOIN test_runs " . + "ON case_runs.run_id = test_runs.run_id"); + $f = "test_runs.run_id"; + }, + "^run_plan_id," => sub { + $f = "test_runs.plan_id"; + }, + "^run_case_id," => sub { + push(@supptables, + "INNER JOIN test_case_runs AS case_runs " . + "ON test_runs.run_id = case_runs.run_id"); + push(@supptables, + "INNER JOIN test_cases " . + "ON case_runs.case_id = test_cases.case_id"); + $f = "test_cases.case_id"; + }, + "^caserun_plan_id," => sub { + push(@supptables, + "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + $f = "test_plans.plan_id"; + }, + "^case_prod," => sub { + push(@supptables, + "INNER JOIN test_case_plans AS case_plans " . + "ON test_cases.case_id = case_plans.case_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON case_plans.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + if ($cgi->param('product_id')){ + $f = "test_plans.product_id"; + } + else { + $f = "products.name"; + } + + }, + "^classification," => sub { + if ($obj eq 'run'){ + push(@supptables, + "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + } + elsif ($obj eq 'case'){ + push(@supptables, + "INNER JOIN test_case_plans AS case_plans " . + "ON test_cases.case_id = case_plans.case_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON case_plans.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + } + elsif ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + } + elsif ($obj eq 'environment'){ + push(@supptables, + "INNER JOIN products + ON test_environments.product_id = products.id"); + } + else{ + push(@supptables, + "INNER JOIN products ". + "ON test_". $obj ."s.product_id = products.id"); + } + push(@supptables, + "INNER JOIN classifications " . + "ON products.classification_id = classifications.id"); + $f = "classifications.name"; + }, + "^caserun_prod," => sub { + push(@supptables, + "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + push(@supptables, + "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + if ($cgi->param('product_id')){ + $f = "test_plans.product_id"; + } + else { + $f = "products.name"; + } + + }, + "^run_prod," => sub { + push(@supptables, + "INNER JOIN test_plans " . + "ON test_runs.plan_id = test_plans.plan_id"); + push(@supptables, + "INNER JOIN products " . + "ON test_plans.product_id = products.id"); + if ($cgi->param('product_id')){ + $f = "test_plans.product_id"; + } + else { + $f = "products.name"; + } + }, + "^stop_date," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + $f = "test_runs.stop_date"; + } + }, + "^run_product_version," => sub { + push(@supptables, + "INNER JOIN test_runs " . + "ON test_case_runs.run_id = test_runs.run_id"); + $f = "test_runs.product_version"; + }, + "^(author|manager|default_tester)," => sub { + push(@supptables, + "INNER JOIN profiles AS map_$1 " . + "ON test_". $obj ."s.". $1 ."_id = map_$1.userid"); + $f = "map_$1.login_name"; + }, + "^(assignee|testedby)," => sub { + if ($obj eq 'run'){ + push(@supptables, + "LEFT JOIN test_case_runs AS case_run " . + "ON case_run.run_id = test_runs.run_id"); + push(@supptables, + "LEFT JOIN profiles AS map_$1 " . + "ON map_$1.userid = case_run.". $1 ); + } + else { + push(@supptables, + "LEFT JOIN profiles AS map_$1 " . + "ON test_". $obj ."s.". $1 ." = map_$1.userid"); + } + $f = "map_$1.login_name"; + + }, + ",isnotnull" => sub { + $term = "$ff is not null"; + }, + ",isnull" => sub { + $term = "$ff is null"; + }, + ",equals" => sub { + $term = "$ff = $q"; + }, + ",notequals" => sub { + $term = "$ff != $q"; + }, + ",casesubstring" => sub { + $term = $dbh->sql_position($q, $ff) . " > 0"; + }, + ",substring" => sub { + $term = $dbh->sql_position(lc($q), "LOWER($ff)") . " > 0"; + }, + ",substr" => sub { + $funcsbykey{",substring"}->(); + }, + ",notsubstring" => sub { + $term = $dbh->sql_position(lc($q), "LOWER($ff)") . " = 0"; + }, + ",regexp" => sub { + $term = "$ff " . $dbh->sql_regexp() . " $q"; + }, + ",notregexp" => sub { + $term = "$ff " . $dbh->sql_not_regexp() . " $q"; + }, + ",lessthan" => sub { + $term = "$ff < $q"; + }, + ",greaterthan" => sub { + $term = "$ff > $q"; + }, + ",anyexact" => sub { + my @list; + foreach my $w (split(/,/, $v)) { + $q = $dbh->quote($w); + trick_taint($q); + push(@list, $q); + } + if (@list) { + $term = "$ff IN (" . join (',', @list) . ")"; + } + }, + ",between" => sub { + $v =~ /(\d+)[\s-]+(\d+)/; + + $term = "$ff BETWEEN $1 AND $2"; + }, + ",anywordssubstr" => sub { + $term = join(" OR ", @{GetByWordListSubstr($ff, $v)}); + }, + ",allwordssubstr" => sub { + $term = join(" AND ", @{GetByWordListSubstr($ff, $v)}); + }, + ",nowordssubstr" => sub { + my @list = @{GetByWordListSubstr($ff, $v)}; + if (@list) { + $term = "NOT (" . join(" OR ", @list) . ")"; + } + }, + ",anywords" => sub { + $term = join(" OR ", @{GetByWordList($ff, $v)}); + }, + ",allwords" => sub { + $term = join(" AND ", @{GetByWordList($ff, $v)}); + }, + ",nowords" => sub { + my @list = @{GetByWordList($ff, $v)}; + if (@list) { + $term = "NOT (" . join(" OR ", @list) . ")"; + } + }, + ",notag" => sub { + $term = "test_". $obj ."s.". $obj ."_id NOT IN (SELECT junc.". $obj ."_id FROM test_". $obj ."_tags AS junc JOIN test_tags AS junc_tags ON junc.tag_id = junc_tags.tag_id WHERE junc_tags.tag_name = " . $q .")"; + }, + ); + + my $chfieldfrom = trim(lc($cgi->param('chfieldfrom'))) || ''; + my $chfieldto = trim(lc($cgi->param('chfieldto'))) || ''; + $chfieldfrom = '' if ($chfieldfrom eq 'now'); + $chfieldto = '' if ($chfieldto eq 'now'); + my @chfield = $cgi->param('chfield_type'); + my $chvalue = trim($cgi->param('chfieldvalue')) || ''; + + if ($chfieldfrom ne '' || $chfieldto ne '') { + my $sql_chfrom = $chfieldfrom ? $dbh->quote(SqlifyDate($chfieldfrom)):''; + my $sql_chto = $chfieldto ? $dbh->quote(SqlifyDate($chfieldto)) :''; + my $sql_chvalue = $chvalue ne '' ? $dbh->quote($chvalue) : ''; + trick_taint($sql_chvalue); + if(!@chfield) { + push(@supptables, + "INNER JOIN test_" . $obj ."_activity " . + "ON test_". $obj ."s.". $obj ."_id = test_" . $obj ."_activity." . $obj ."_id"); + push(@wherepart, "test_" . $obj ."_activity.changed >= $sql_chfrom") if ($sql_chfrom); + push(@wherepart, "test_" . $obj ."_activity.changed <= $sql_chto") if ($sql_chto); + } else { + my $bug_creation_clause; + my @list; + my @actlist; + foreach my $f (@chfield) { + if ($f eq "[Creation]") { + my @l; + if ($obj eq 'run'){ + push(@l, "test_" . $obj ."s.start_date >= $sql_chfrom") if($sql_chfrom); + push(@l, "test_" . $obj ."s.start_date <= $sql_chto") if($sql_chto); + } + else{ + push(@l, "test_" . $obj ."s.creation_date >= $sql_chfrom") if($sql_chfrom); + push(@l, "test_" . $obj ."s.creation_date <= $sql_chto") if($sql_chto); + } + $bug_creation_clause = "(" . join(' AND ', @l) . ")"; + } + elsif ($f eq "text"){ + push(@supptables, + "INNER JOIN test_" . $obj ."_texts " . + "ON test_". $obj ."s.". $obj ."_id = test_" . $obj ."_texts." . $obj ."_id"); + push(@wherepart, "test_" . $obj ."_texts.creation_ts >= $sql_chfrom") if ($sql_chfrom); + push(@wherepart, "test_" . $obj ."_texts.creation_ts <= $sql_chto") if ($sql_chto); + + } + else { + push(@actlist, $f); + } + } + + # @actlist won't have any elements if the only field being searched + # is [Bug creation] (in which case we don't need bugs_activity). + if(@actlist) { + my $extra = " actcheck." . $obj . "_id = test_" . $obj ."s." . $obj . "_id"; + push(@list, "(actcheck.changed IS NOT NULL)"); + if($sql_chfrom) { + $extra .= " AND actcheck.changed >= $sql_chfrom"; + } + if($sql_chto) { + $extra .= " AND actcheck.changed <= $sql_chto"; + } + if($sql_chvalue) { + $extra .= " AND actcheck.newvalue = $sql_chvalue"; + } + push(@supptables, "LEFT JOIN test_" . $obj ."_activity AS actcheck " . + "ON $extra AND actcheck.fieldid IN (" . + join(",", @actlist) . ")"); + } + + # Now that we're done using @list to determine if there are any + # regular fields to search (and thus we need bugs_activity), + # add the [Bug creation] criterion to the list so we can OR it + # together with the others. + push(@list, $bug_creation_clause) if $bug_creation_clause; + + push(@wherepart, "(" . join(" OR ", @list) . ")") if scalar @list; + } + } + if ($cgi->param('permuser')) { + my $permuser = login_to_id($cgi->param('permuser'), "BARF"); + my $perm = $cgi->param('permission'); + detaint_natural($permuser); + push(@wherepart, "test_plan_permissions.userid = $permuser"); + push(@specialchart, ["plan_perms", 'equals', $perm]); + } + if ($cgi->param('bug_keywords')) { + my $t = $cgi->param('bug_keywords_type'); + if (!$t || $t eq "or") { + $t = "anywords"; + } + push(@specialchart, ["bug_keywords", $t, $cgi->param('keywords')]); + } + if ($cgi->param('case_id')) { + my $type = "anyexact"; + if ($cgi->param('caseidtype')) + { + if ($cgi->param('caseidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('caseidtype') + } + } + elsif ($cgi->param('case_id') =~ /-/){ + $type = "between"; + } + if ($obj eq 'run'){ + push(@specialchart, ["run_case_id", $type, join(',', $cgi->param('case_id'))]); + } + elsif ($obj eq 'plan'){ + push(@specialchart, ["plan_case_id", $type, join(',', $cgi->param('case_id'))]); + } + else{ + push(@specialchart, ["case_id", $type, join(',', $cgi->param('case_id'))]); + } + } + if ($cgi->param('run_id')) { + my $type = "anyexact"; + if ($cgi->param('runidtype')) + { + if ($cgi->param('runidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('runidtype') + } + } + elsif ($cgi->param('run_id') =~ /-/){ + $type = "between"; + } + if ($obj eq 'case'){ + push(@specialchart, ["cases_in_runs", $type, join(',', $cgi->param('run_id'))]); + } + else { + push(@specialchart, ["run_id", $type, join(',', $cgi->param('run_id'))]); + } + } + if ($cgi->param('plan_id')) { + my $type = "anyexact"; + if ($cgi->param('planidtype')) + { + if ($cgi->param('planidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('planidtype') + } + } + elsif ($cgi->param('plan_id') =~ /-/){ + $type = "between"; + } + if ($obj eq 'case'){ + push(@specialchart, ["case_plan_id", $type, join(',', $cgi->param('plan_id'))]); + if ($cgi->param('exclude')){ + my @runs = split(/,/, $cgi->param('exclude')); + foreach (@runs){ + detaint_natural($_); + } + my $exclusions = $dbh->selectcol_arrayref('SELECT DISTINCT case_id FROM test_case_runs WHERE run_id IN ('. join(',', @runs) .')'); + push @wherepart, 'test_cases.case_id NOT IN ('. join(',', @$exclusions) .')' if scalar @$exclusions > 0; + } + } + elsif ($obj eq 'run'){ + push(@specialchart, ["run_plan_id", $type, join(',', $cgi->param('plan_id'))]); + } + elsif ($obj eq 'case_run'){ + push(@specialchart, ["caserun_plan_id", $type, join(',', $cgi->param('plan_id'))]); + } + else{ + push(@specialchart, ["plan_id", $type, join(',', $cgi->param('plan_id'))]); + } + } + if ($cgi->param('bug_id')) { + my $type = "anyexact"; + if ($cgi->param('bugidtype') && $cgi->param('bugidtype') eq 'exclude') { + $type = "nowords"; + } + elsif ($cgi->param('bug_id') =~ /-/){ + $type = "between"; + } + push(@specialchart, ["bug", $type, join(',', $cgi->param('bug_id'))]); + } + if ($cgi->param("product_id") || $cgi->param("product")){ + my $attribute = $cgi->param("product_id") ? "product_id" : "product"; + my $type = "anyexact"; + if ($cgi->param('prodidtype') && $cgi->param('prodidtype') eq 'exclude') { + $type = "nowords"; + } + if ($obj eq 'run'){ + push(@specialchart, ["run_prod", $type, join(',', $cgi->param($attribute))]); + } + elsif ($obj eq 'case'){ + push(@specialchart, ["case_prod", $type, join(',', $cgi->param($attribute))]); + } + elsif ($obj eq 'case_run'){ + push(@specialchart, ["caserun_prod", $type, join(',', $cgi->param($attribute))]); + } + elsif ($obj eq 'environment'){ + push(@specialchart, ["env_products", $type, join(',', $cgi->param($attribute))]); + } + else{ + if ($cgi->param("product")){ + push(@specialchart, ["prod_name", $type, join(',', $cgi->param($attribute))]); + } + else{ + push(@specialchart, ["product_id", $type, join(',', $cgi->param($attribute))]); + } + } + } + my $email = trim($cgi->param("bug_email")); + my $email_type = $cgi->param("bug_emailtype"); + if ($email_type && $email_type eq "exact") { + $email_type = "anyexact"; + foreach my $name (split(',', $email)) { + $name = trim($name); + if ($name) { + login_to_id($name, "BARF"); + } + } + } + + my @bug_clist; + foreach my $field ("assigned_to", "reporter", "cc", "qa_contact", "infoprovider") { + if ($cgi->param("bug_email$field")) { + push(@bug_clist, $field, $type, $email); + } + } + if ($cgi->param("bug_emaillongdesc")) { + push(@bug_clist, "commenter", $type, $email); + } + if (@bug_clist) { + push(@specialchart, \@bug_clist); + } + + # Check the Multi select fields and add them to the chart + my @legal_fields = ("case_status_id", "category", "category_id", "priority_id", + "component", "isautomated", "alias", "case_run_status_id", + "default_product_version", "run_product_version", "type_id", + "build", "build_id", "environment_id", "milestone", "env_products", + "env_categories", "env_elements", "env_properties", + "env_expressions", "case_status", "priority", "environment", + "plan_type", "case_run_status", "classification", + "bug_severity", "bug_resolution","bug_priority", "bug_status", + "bug_rep_platform","bug_os_sys"); + + foreach my $field ($cgi->param()) { + if (lsearch(\@legal_fields, $field) != -1) { + push(@specialchart, [$field, $cgi->param($field."_type") || "anyexact", + join(',', $cgi->param($field))]); + } + } + # 19.01.2007 - Changed multiselct version fields to 'version'. + # Changing them above in @legal_fields could break API funtionality + # So we redifine them here. + if (defined $cgi->param('version')){ + my $field; + if ($obj eq 'case_run'){ + $field = 'run_product_version'; + } + elsif ($obj eq 'run'){ + $field = 'product_version'; + } + elsif ($obj eq 'plan'){ + $field = 'default_product_version'; + } + push(@specialchart, [$field, "anyexact", join(',', $cgi->param('version'))]); + } + if (defined $cgi->param('run_status')){ + my @sta = $cgi->param('run_status'); + unless (scalar @sta > 1){ + if ($cgi->param('run_status') == 1){ + push(@specialchart, ['stop_date', 'isnull', 'null']); + } + else { + push(@specialchart, ['stop_date', 'isnotnull', 'null']); + } + } + } + if ($cgi->param('closed_from') || $cgi->param('closed_to')){ + if ($obj eq 'case_run'){ + my $closedfrom = $cgi->param('closed_from'); + my $closedto = $cgi->param('closed_to'); + trick_taint($closedfrom); + trick_taint($closedto); + push(@specialchart, ['close_date', 'greaterthan', SqlifyDate($closedfrom)||'']); + push(@specialchart, ['close_date', 'lessthan', SqlifyDate($closedto)||'']); + } + } + if (defined $cgi->param('close_date')){ + my @sta = $cgi->param('close_date'); + unless (scalar @sta > 1){ + if ($cgi->param('close_date') == 1){ + push(@specialchart, ['close_date', 'isnotnull', 'null']); + } + else { + push(@specialchart, ['close_date', 'isnull', 'null']); + } + } + } + # Check the tags and add them to the chart + if ($cgi->param('tags')) { + my $t = $cgi->param('tags_type'); + if (!$t || $t eq "or") { + $t = "anywords"; + } + push(@specialchart, ["tags", $t, $cgi->param('tags')]); + } + # Check for author + my @clist; + foreach my $profile ("author", "manager", "default_tester", + "assignee", "testedby"){ + $t = $cgi->param($profile . "_type") || ''; + if ($t eq "exact" || $t eq '') { + $t = "anyexact"; + if ($cgi->param($profile)){ + foreach my $name (split(',', $cgi->param($profile))) { + $name = trim($name); + if ($name) { + login_to_id($name); + trick_taint($name); + } + } + } + } + if ($cgi->param($profile)){ + my $user = trim($cgi->param($profile)); + trick_taint($user); + if ($cgi->param('andor')){ + push(@specialchart, [$profile, $t, $user]); + } + else{ + push(@clist, $profile, $t, $user); + } + } + } + if (@clist) { + push(@specialchart, \@clist); + } + + # check static text fields + foreach my $f ("case_summary", "summary", "tcaction", "tceffect", "script", + "requirement", "name", "plan_text", "environment_name", + "notes", "env_value_selected","bug_short_desc","bug_long_desc", + "bug_file_loc","bug_status_whiteboard","bug_keywords", + "env_category","env_element","env_property","env_value", + "start_date", "stop_date") { + if (defined $cgi->param($f)) { + my $s = trim($cgi->param($f)); + if ($s ne "") { + trick_taint($s); + my $type = $cgi->param($f . "_type") || 'allwordssubstr'; + push(@specialchart, [$f, $type, $s]); + } + } + } + if ($obj eq 'plan'){ + unless ($cgi->param('isactive')){ + push @wherepart, 'test_plans.isactive = 1'; + } + } + if ($obj eq 'environment'){ + if ($cgi->param('isactive')){ + push @wherepart, 'test_environments.isactive = 1'; + } + } + if ($obj eq 'case_run'){ + unless ($cgi->param('isactive')){ + push @wherepart, 'test_case_runs.iscurrent = 1'; + } + } + if ($obj eq 'case'){ + if ($cgi->param('isactive')){ + push @wherepart, 'case_runs.iscurrent = 1'; + } + } + + my @funcnames; + while (@funcdefs) { + my $key = shift(@funcdefs); + my $value = shift(@funcdefs); + if ($key =~ /^[^,]*$/) { + die "All defs in %funcs must have a comma in their name: $key"; + } + if (exists $funcsbykey{$key}) { + die "Duplicate key in %funcs: $key"; + } + $funcsbykey{$key} = $value; + push(@funcnames, $key); + } + + # first we delete any sign of "Chart #-1" from the HTML form hash + # since we want to guarantee the user didn't hide something here + my @badcharts = grep /^(field|type|value)-1-/, $cgi->param(); + foreach my $field (@badcharts) { + $cgi->delete($field); + } + + # now we take our special chart and stuff it into the form hash + my $chart = -1; + my $row = 0; + foreach my $ref (@specialchart) { + my $col = 0; + while (@$ref) { + $cgi->param("field$chart-$row-$col", shift(@$ref)); + $cgi->param("type$chart-$row-$col", shift(@$ref)); + $cgi->param("value$chart-$row-$col", shift(@$ref)); + if ($debug) { + print "field$chart-$row-$col => " . $cgi->param("field$chart-$row-$col") ." | ". "type$chart-$row-$col => " . $cgi->param("type$chart-$row-$col") ." | ". "value$chart-$row-$col => " . $cgi->param("value$chart-$row-$col") . "
\n"; + } + $col++; + + } + $row++; + } + if ($debug){ + foreach my $p ($cgi->param){ + print "PARAM: $p => " . $cgi->param($p) . "
"; + } + } + # get a list of field names to verify the user-submitted chart fields against + my $ref = $dbh->selectall_arrayref("SELECT name, fieldid FROM test_fielddefs"); + foreach my $list (@{$ref}) { + my ($name, $id) = @{$list}; + $chartfields{$name} = $id; + } + + $row = 0; + for ($chart=-1 ; + $chart < 0 || $cgi->param("field$chart-0-0") ; + $chart++) { + $chartid = $chart >= 0 ? $chart : ""; + my @chartandlist = (); + for ($row = 0 ; + $cgi->param("field$chart-$row-0") ; + $row++) { + my @orlist; + for (my $col = 0 ; + $cgi->param("field$chart-$row-$col") ; + $col++) { + $f = $cgi->param("field$chart-$row-$col") || "noop"; + $t = $cgi->param("type$chart-$row-$col") || "noop"; + $v = $cgi->param("value$chart-$row-$col"); + $v = "" if !defined $v; + $v = trim($v); + if ($f eq "noop" || $t eq "noop" || $v eq "") { + next; + } + # chart -1 is generated by other code above, not from the user- + # submitted form, so we'll blindly accept any values in chart -1 + if ((!$chartfields{$f}) && ($chart != -1) && ! grep($f, @funcnames)) { + ThrowCodeError("invalid_field_name", {field => $f}); + } + + # This is either from the internal chart (in which case we + # already know about it), or it was in %chartfields, so it is + # a valid field name, which means that it's ok. + trick_taint($f); + $q = $dbh->quote($v); + # Now that the value has been quoted, we can detaint it. + trick_taint($q); + my $rhs = $v; + $rhs =~ tr/,//; + my $func; + $term = undef; + foreach my $key (@funcnames) { + if ("$f,$t,$rhs" =~ m/$key/) { + my $ref = $funcsbykey{$key}; + if ($debug) { + print "

$key ($f , $t , $rhs ) => "; + } + $ff = $f; + if ($f !~ /\./) { + $ff = "test_". $obj ."s.$f"; + } + &$ref; + if ($debug) { + print "$f , $t , $v , $term

"; + } + if ($term) { + last; + } + } + } + if ($term) { + push(@orlist, $term); + } + else { + # This field and this type don't work together. + ThrowCodeError("field_type_mismatch", + { field => $cgi->param("field$chart-$row-$col"), + type => $cgi->param("type$chart-$row-$col"), + }); + } + } + if (@orlist) { + @orlist = map("($_)", @orlist) if (scalar(@orlist) > 1); + push(@chartandlist, "(" . join(" OR ", @orlist) . ")"); + } + } + if (@chartandlist) { + if ($cgi->param("negate$chart")) { + push(@andlist, "NOT(" . join(" AND ", @chartandlist) . ")"); + } else { + push(@andlist, "(" . join(" AND ", @chartandlist) . ")"); + } + } + } + + # The ORDER BY clause goes last, but can require modifications + # to other parts of the query, so we want to create it before we + # write the FROM clause. + foreach my $orderitem (@inputorder) { + # Some fields have 'AS' aliases. The aliases go in the ORDER BY, + # not the whole fields. + # XXX - Ideally, we would get just the aliases in @inputorder, + # and we'd never have to deal with this. + if ($orderitem =~ /\s+AS\s+(.+)$/i) { + $orderitem = $1; + } + BuildOrderBy($orderitem, \@orderby); + } + # Now JOIN the correct tables in the FROM clause. + # This is done separately from the above because it's + # cleaner to do it this way. + foreach my $orderitem (@inputorder) { + # Grab the part without ASC or DESC. + my @splitfield = split(/\s+/, $orderitem); + if ($specialorderjoin{$splitfield[0]}) { + push(@supptables, $specialorderjoin{$splitfield[0]}); + } + } + if ($debug){ + print "
";
+        print join("\n", @supptables);
+        print "
"; + } + my %suppseen = ("test_". $obj ."s" => 1); + my $suppstring = "test_". $obj ."s"; + my @supplist = (" "); + foreach my $str (@supptables) { + if (!$suppseen{$str}) { + if ($str =~ /^(LEFT|INNER|RIGHT)\s+JOIN/i) { + $str =~ /^(.*?)\s+ON\s+(.*)$/i; + my ($leftside, $rightside) = ($1, $2); + if ($suppseen{$leftside}) { + $supplist[$suppseen{$leftside}] .= " AND ($rightside)" unless ($rightside eq 'case_bugs.bug_id = bugs.bug_id' || 'test_runs.environment_id = test_environment_map.environment_id'); + } else { + $suppseen{$leftside} = scalar @supplist; + push @supplist, " $leftside ON ($rightside)"; + } + } else { + # Do not accept implicit joins using comma operator + # as they are not DB agnostic + # ThrowCodeError("comma_operator_deprecated"); + $suppstring .= ", $str"; + $suppseen{$str} = 1; + } + } + } + $suppstring .= join('', @supplist); + + # Make sure we create a legal SQL query. + @andlist = ("1 = 1") if !@andlist; + print "WHEREPART: " . Data::Dumper::Dumper(\@wherepart) if $debug; + my $query; + if ($self->{'fields'}){ + $query = "SELECT $distinct". join(",", @{$self->{'fields'}}); + foreach my $qfield (@{$self->{'fields'}}){ + # Remove the magic placeholder (See Report.pm) + push @groupby, $qfield unless $qfield == '42217354'; + } + } + else { + $query = "SELECT $distinct test_". $obj ."s.". $obj. "_id"; + } + if (scalar @orderby){ + $query .= ',' . join(',', @orderby); + } + $query .= " FROM $suppstring"; + $query .= " WHERE " . join(' AND ', (@wherepart, @andlist)); + if ($obj eq 'case_run' && $cgi->param('addcases')){ + my $addcases = $cgi->param('addcases'); + my $run_id = $cgi->param('run_id'); + trick_taint($addcases); + trick_taint($run_id); + $query .= ' OR (test_case_runs.case_id IN ('. $addcases .') AND test_case_runs.iscurrent = 1 AND test_case_runs.run_id = '. $run_id . ')'; + } + + + foreach my $field (@fields, @orderby) { + next if ($field =~ /(AVG|SUM|COUNT|MAX|MIN|VARIANCE)\s*\(/i || + $field =~ /^\d+$/ || $field eq "bugs.bug_id" || + $field =~ /^relevance/); + if ($field =~ /.*AS\s+(\w+)$/i) { + push(@groupby, $1) if !grep($_ eq $1, @groupby); + } else { + push(@groupby, $field) if !grep($_ eq $field, @groupby); + } + } + if (scalar @groupby && $cgi->param('report')){ + $query .= " " . $dbh->sql_group_by("test_${obj}s.${obj}_id", join(', ', @groupby)); + } + elsif ($cgi->param('report')) { + $query .= " " . $dbh->sql_group_by("test_${obj}s.${obj}_id"); + } + + + if (@having) { + $query .= " HAVING " . join(" AND ", @having); + } + + if (@orderby) { + $query .= " ORDER BY " . join(',', @orderby) . ' ' . $sortdir; #This works for now since there is only single field sort + } + if(defined $start){ + $query .= " LIMIT $limit OFFSET $start"; + } + elsif (defined $page){ + $query .= " LIMIT $pagesize OFFSET ". $page*$pagesize; + } + if ($debug) { + print "

" . $query . "

\n"; + } + + $self->{'sql'} = $query; + +} + +sub query { + my $self = shift; + return $self->{'sql'}; +} + +sub SqlifyDate { + my ($str) = @_; + $str = "" if !defined $str; + if ($str eq "" || lc($str) eq 'now') { + my ($sec, $min, $hour, $mday, $month, $year, $wday) = localtime(time()); + return sprintf("%4d-%02d-%02d %02d:%02d:%02d", $year+1900, $month+1, $mday, $hour, $min, $sec); + } + + + if ($str =~ /^(-|\+)?(\d+)([hHdDwWmMyY])$/) { # relative date + my ($sign, $amount, $unit, $date) = ($1, $2, lc $3, time); + my ($sec, $min, $hour, $mday, $month, $year, $wday) = localtime($date); + if ($sign && $sign eq '+') { $amount = -$amount; } + if ($unit eq 'w') { # convert weeks to days + $amount = 7*$amount + $wday; + $unit = 'd'; + } + if ($unit eq 'd') { + $date -= $sec + 60*$min + 3600*$hour + 24*3600*$amount; + return time2str("%Y-%m-%d %H:%M:%S", $date); + } + elsif ($unit eq 'y') { + return sprintf("%4d-01-01 00:00:00", $year+1900-$amount); + } + elsif ($unit eq 'm') { + $month -= $amount; + while ($month<0) { $year--; $month += 12; } + return sprintf("%4d-%02d-01 00:00:00", $year+1900, $month+1); + } + elsif ($unit eq 'h') { + # Special case 0h for 'beginning of this hour' + if ($amount == 0) { + $date -= $sec + 60*$min; + } else { + $date -= 3600*$amount; + } + return time2str("%Y-%m-%d %H:%M:%S", $date); + } + return undef; # should not happen due to regexp at top + } + my $date = str2time($str); + if (!defined($date)) { + ThrowUserError("illegal_date", { date => $str }); + } + return time2str("%Y-%m-%d %H:%M:%S", $date); +} + + +sub GetByWordList { + my ($field, $strs) = (@_); + my @list; + my $dbh = Bugzilla->dbh; + + foreach my $w (split(/[\s,]+/, $strs)) { + my $word = $w; + if ($word ne "") { + $word =~ tr/A-Z/a-z/; + $word = $dbh->quote(quotemeta($word)); + trick_taint($word); + $word =~ s/^'//; + $word =~ s/'$//; + $word = '(^|[^a-z0-9])' . $word . '($|[^a-z0-9])'; + push(@list, "$field " . $dbh->sql_regexp() . " '$word'"); + } + } + + return \@list; +} + +# Support for "any/all/nowordssubstr" comparison type ("words as substrings") +sub GetByWordListSubstr { + my ($field, $strs) = (@_); + my @list; + my $dbh = Bugzilla->dbh; + + foreach my $word (split(/[\s,]+/, $strs)) { + next if $word eq ""; + $word = $dbh->quote($word); + trick_taint($word); + push(@list, $dbh->sql_position(lc($word), "LOWER($field)") . " > 0"); + } + + return \@list; +} + +=head1 SEE ALSO + +Testopia::Table Bugzilla::Search + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; diff --git a/Bugzilla/Testopia/Table.pm b/Bugzilla/Testopia/Table.pm new file mode 100644 index 0000000..d498472 --- /dev/null +++ b/Bugzilla/Testopia/Table.pm @@ -0,0 +1,515 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +=head1 NAME + +Bugzilla::Testopia::Table - Produces display tables for Testopia lists + +=head1 DESCRIPTION + +A table is generated as a result of a query. This module returns +a list of Testopia objects that were queried for. It supports +pagination of data as well as sorting. It takes the following +arguments: + +=over + +=item type - one of 'case', 'plan', 'run', 'caserun', or 'environment' + +=item url - the cgi file that is calling this + +=item cgi - a CGI object + +=item list - A reference to a list + +=item query - An SQL query string, usually generated by Search.pm + +=back + +=head1 SYNOPSIS + + $table = Bugzilla::Testopia::Table->new($type, $url, $cgi, $list, $query); + +=cut + +package Bugzilla::Testopia::Table; + +use strict; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCaseRun; + +############################### +#### Initialization #### +############################### + +# For use in sorting functions which do not allow arguments +our $field; +our $reverse; + +=head1 METHODS + +=head2 new + +Instantiates a new table object + +=cut + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + + my $self = {}; + bless($self, $class); + + $self->init(@_); + + return $self; +} + +=head2 init + +Private constructor for this class + +=cut + +sub init { + my $self = shift; + my ($type, $url, $cgi, $list, $query) = @_; + $self->{'user'} = Bugzilla->user; + $self->{'type'} = $type || ThrowCodeError('bad_arg', + {argument => 'type', + function => 'Testopia::Table::_init'}); + $self->{'url_loc'} = $url; + $self->{'cgi'} = $cgi; + my $debug = $cgi->param('debug') if $cgi; + my @list; + if ($query){ + # For paging we need to know the total number of items + # but Search.pm returns a query with a subset + my $countquery = $query; + $countquery =~ s/ LIMIT.*$//; + + my $dbh = Bugzilla->switch_to_shadow_db(); + + my $count_res = $dbh->selectcol_arrayref($countquery); + my $count = scalar @$count_res; + print "

Total rows: $count

" if $debug; + + $self->{'list_count'} = $count; + my @ids; + my $list = $dbh->selectcol_arrayref($query); + + $dbh = Bugzilla->switch_to_main_db(); + + foreach my $id (@$list){ + my $o; + if ($type eq 'case'){ + $o = Bugzilla::Testopia::TestCase->new($id); + } + elsif ($type eq 'plan'){ + $o = Bugzilla::Testopia::TestPlan->new($id); + } + elsif ($type eq 'run'){ + $o = Bugzilla::Testopia::TestRun->new($id); + } + elsif ($type eq 'case_run'){ + $o = Bugzilla::Testopia::TestCaseRun->new($id); + } + elsif ($type eq 'environment'){ + $o = Bugzilla::Testopia::Environment->new($id); + } + push (@ids, $id); + push (@list, $o); + } + $self->{'list'} = \@list; + $self->{'view_count'} = scalar @list; + $self->{'id_list'} = join(",", @$count_res); + } + if ($cgi){ + $self->{'viewall'} = $cgi->param('viewall'); + $self->{'page'} = $cgi->param('page') || 0; + } + + print $query if $debug; + exit if $debug; +# elsif (!$query && !$list){ +# my @list; +# foreach my $id (split(",", $self->get_saved_list())){ +# if ($self->{'type'} eq 'case'){ +# my $o = Bugzilla::Testopia::TestCase->new($id); +# push @list, $o; +# $o->category; +# $o->status; +# $o->priority; +# } +# elsif ($self->{'type'} eq 'plan'){ +# my $o = Bugzilla::Testopia::TestPlan->new($id); +# push @list, $o; +# $o->test_case_count; +# $o->test_run_count; +# } +# elsif ($self->{'type'} eq 'run'){ +# my $o = Bugzilla::Testopia::TestCase->new($id); +# push @list, $o; +## $o->category; +## $o->status; +## $o->priority; +# } +# elsif ($self->{'type'} eq 'caserun'){ +# my $o = Bugzilla::Testopia::TestCase->new($id); +# push @list, $o; +## $o->category; +## $o->status; +## $o->priority; +# } +# elsif ($self->{'type'} eq 'attachment'){ +# my $o = Bugzilla::Testopia::TestCase->new($id); +# push @list, $o; +## $o->category; +## $o->status; +## $o->priority; +# } +# else { +# ThrowUserError('unknown-type'); +# } +# } +# $self->{'list'} = \@list; +# } +# else { +# $self->{'list'} = $list; +# } +# my @params = split(":", $cgi->cookie('TESTORDER')); +# $self->{'last_sort'} = shift @params || undef; +# $self->{'reverse_sort'} = shift @params || undef; +# +# #### SORT #### +# # This is very inefficient. It would be much better to have +# # the database do this. +# my $order = $cgi->param('order'); +# if ($order){ +# $self->sort_fields($order); +# $self->{'reverse_sort'} = ($self->{'reverse_sort'} ? 1 : 0); +# } +# + #### SAVE #### + # Save the list of testcases for use in paginating and sorting + $self->save_list; +# +# #### SPLICE #### +# # If we are using a paged view of the data we split it up here +# $self->get_page($self->{'page'}); + +} + +############################### +#### Methods #### +############################### + +=head2 save_list + +Saves the last list to the database as a hidden saved search +Used only in list context + +=cut + +sub save_list { + my $self = shift; + return if ($self->{'user'}->id == 0); +# my @ids; +# foreach my $i (@{$self->{'list'}}){ +# push @ids, $i->id; +# } +# my $list = join(",", @ids); + my $dbh = Bugzilla->dbh; + if ($self->{'id_list'}){ + $dbh->bz_start_transaction(); + my ($is) = $dbh->selectrow_array( + "SELECT 1 FROM test_named_queries + WHERE userid = ? AND name = ?", undef, + ($self->{'user'}->id, "__". $self->{'type'} ."__")); + + if ($is) { + $dbh->do("UPDATE test_named_queries SET query = ? + WHERE userid = ? AND name = ?", undef, + (join(",", $self->{'id_list'}), $self->{'user'}->id, "__". $self->{'type'} ."__")); + } + else { + $dbh->do("INSERT INTO test_named_queries (userid, name, isvisible, query) VALUES(?,?,?,?)", undef, + ($self->{'user'}->id, "__". $self->{'type'} ."__", 0, join(",", $self->{'id_list'}))); + } + $dbh->bz_commit_transaction(); + } +# $self->{'list_count'} = scalar @ids unless $self->{'query'}; +} + +=head2 get_saved_list + +Retrieves a saved list from the database +Used only in list context + +=cut + +sub get_saved_list { + my $self = shift; + return undef if ($self->{'user'}->id == 0); + my $type = shift || $self->{'type'}; + my $dbh = Bugzilla->dbh; + my ($list) = $dbh->selectrow_array( + "SELECT query FROM test_named_queries + WHERE userid = ? AND name = ?", undef, + ($self->{'user'}->id, "__". $type ."__")); + my @list = split(',', $list); + return \@list; +} + +# used by the sort function to check which field to sort on +# we turn off warnings to supress the nonnumeric vs numeric junk +sub sort_fields { + no warnings; + if ($field eq 'category'){ + if ($reverse){ + $a->category->name cmp $b->category->name; + } + else { + $b->category->name cmp $a->category->name; + } + } + elsif ($field eq 'plans'){ + if ($reverse){ + scalar @{$a->plans} <=> scalar @{$b->plans}; + } + else { + scalar @{$b->plans} <=> scalar @{$a->plans}; + } + } + else{ + if ($reverse){ + $a->{$field} cmp $b->{$field}; + } + else { + $b->{$field} cmp $a->{$field}; + } + } + use warnings; +} + +sub sort_list { + my $self = shift; + $field = shift; + $reverse = $self->{'reverse_sort'}; + my @list = sort sort_fields @{$self->{'list'}}; + $self->{'list'} = \@list; + return $self->{'list'}; +} + +sub get_page { + my $self = shift; + if ($self->{'viewall'}){ + return $self->{'list'}; + } + my $pagenum = shift || $self->{'page'}; + $self->{'page'} = $pagenum; + my $offset = $pagenum * $self->page_size; + my @list = @{$self->{'list'}}; + @list = splice(@list, $offset, $self->page_size); + $self->{'list'} = \@list; + return $self->{'list'}; +} + +sub get_next{ + my $self = shift; + my ($curr) = @_; + + my $list = $self->get_list; + my $ref = lsearch($curr, $list); + return undef if $ref == -1; + return $list->[$ref]; +} +############################### +#### Accessors #### +############################### + +sub list { return $_[0]->{'list'}; } +sub id_list { return $_[0]->{'id_list'}; } +sub list_count { return $_[0]->{'list_count'}; } +sub view_count { return $_[0]->{'view_count'}; } +sub page { return $_[0]->{'page'}; } +sub url_loc { return $_[0]->{'url_loc'}; } +sub type { return $_[0]->{'type'}; } + +=head2 page_size + +Returns an ineger representing how many items should appear on a page + +=cut + +sub page_size { + my $self = shift; + my $cgi = $self->{'cgi'}; + my $size = $cgi->param('pagesize') || 25; + return $size; +} + +=head2 get_order_url + +Returns a URL query string from a CGI object which is used by +column headers to produce a sort order + +=cut + +sub get_order_url { + my $self = shift; + return $self->get_url('page','order'); +} + +=head2 get_page_url + +Retrns a URL query string from a CGI object which is used by +the page navigation links to move from page to page. + +=cut + +sub get_page_url { + my $self = shift; + my $cgi = $self->{'cgi'}; + return $self->{'url_loc'} ."?". $cgi->canonicalise_query('page', 'pagesize', 'viewall'); +} + +sub get_url { + my ($self, @drops) = @_; + my $cgi = $self->{'cgi'}; + $self->{'url'} = $self->{'url_loc'} ."?". $cgi->canonicalise_query(@drops); + return $self->{'url'}; +} + +sub get_query_part { + my $self = shift; + my $cgi = $self->{'cgi'}; + my @keys = $cgi->param; + my $qstring; + foreach my $key (@keys){ + my @vals = $cgi->param($key); + foreach my $val (@vals){ + $qstring .= $key ."=". url_quote($val) ."&"; + } + } + chop $qstring; + return $qstring +} + +=head2 page_count + +Returns a total count of the number of pages returned by a query. +Determined in part by page_size + +=cut + +sub page_count { + my $self = shift; + if ($self->list_count % $self->page_size){ + use integer; + return ($self->list_count / $self->page_size) + 1; + } + return $self->list_count/$self->page_size; +} + +sub reverse_sort { + my $self = shift; + return $self->{'reverse_sort'}; +} + +sub ajax { + my $self = shift; + return $self->{'ajax'} ? 1 : 0; +} + +sub arrow { + my $self = shift; + if ($self->{'reverse_sort'}) { + $self->{'arrow'} = ''; + } + else { + $self->{'arrow'} = ''; + } + return $self->{'arrow'}; +} + +sub to_yui_json { + my $self = shift; + my $out = ''; + $out .= '{"ResultSet":{'; + $out .= '"totalResultsAvailable":' . $self->list_count .','; + $out .= '"totalResultsReturned":' . $self->page_size .','; + $out .= '"firstResultPosition":' . $self->page * $self->page_size .','; + $out .= '"Result":['; + foreach my $i (@{$self->list}){ + $out .= '{'; + $out .= '"ID":"' . $i->id . '",'; + $out .= '"Name":"' . $i->name . '",'; + $out .= '"Author":"' . $i->author->login . '",'; + $out .= '"Created":"' . $i->creation_date . '",'; + $out .= '"Product":"' . $i->product->name . '",'; + $out .= '"Version":"' . $i->product_version . '",'; + $out .= '"Type":"' . $i->type . '",'; + $out .= '"Cases":"' . $i->test_case_count . '",'; + $out .= '"Runs":"' . $i->test_run_count . '",'; + $out .= '"ClickUrl":"tr_show_plan.cgi?plan_id=' . $i->id; + $out .= '"},'; + } + chomp($out); + $out .= ']}}'; + return $out; + +} + +sub to_ext_json { + my $self = shift; + + my $out = ''; + $out .= '{'; + $out .= '"totalResultsAvailable":' . $self->list_count .','; + $out .= '"Result":['; + foreach my $i (@{$self->list}){ + $out .= $i->TO_JSON . ',' if $i; + } + chop($out) if scalar @{$self->list}; + $out .= ']}'; + return $out; + +} + +=head1 SEE ALSO + +Testopia::Search + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; diff --git a/Bugzilla/Testopia/TestCase.pm b/Bugzilla/Testopia/TestCase.pm new file mode 100644 index 0000000..5a1d058 --- /dev/null +++ b/Bugzilla/Testopia/TestCase.pm @@ -0,0 +1,2398 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Portions taken from processbug.cgi and Bug.pm +# which are copyrighted by +# Terry Weissman +# Dan Mosedale +# Dave Miller +# Christopher Aillon +# Myk Melez +# Jeff Hedlund +# Frederic Buclin +# +# Contributor(s): Greg Hendricks +# Jeff Dayley + +package Bugzilla::Testopia::TestCase; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Bug; +use Bugzilla::User; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Constants; + +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::Attachment; + +use JSON; +use Text::Diff; + +use base qw(Exporter Bugzilla::Object); +@Bugzilla::Testopia::TestCase::EXPORT = qw(lookup_status lookup_status_by_name + lookup_category lookup_category_by_name + lookup_priority lookup_priority_by_value + lookup_default_tester); + +############################### +#### Initialization #### +############################### + +use constant DB_TABLE => "test_cases"; +use constant NAME_FIELD => "alias"; +use constant ID_FIELD => "case_id"; +use constant DB_COLUMNS => qw( + case_id + case_status_id + category_id + priority_id + author_id + default_tester_id + creation_date + estimated_time + isautomated + sortkey + script + arguments + summary + requirement + alias +); + +use constant REQUIRED_CREATE_FIELDS => qw(case_status_id category_id priority_id author_id summary plans); +use constant UPDATE_COLUMNS => qw(case_status_id category_id priority_id default_tester_id + isautomated sortkey script arguments summary requirement + alias estimated_time dependson blocks runs tags components); + +use constant VALIDATORS => { + case_status_id => \&_check_status, + priority_id => \&_check_priority, + default_tester_id => \&_check_tester, + author_id => \&_check_author, + isautomated => \&_check_automated, + sortkey => \&_check_sortkey, + script => \&_check_script, + arguments => \&_check_arguments, + summary => \&_check_summary, + requirement => \&_check_requirements, + alias => \&_check_alias, + estimated_time => \&_check_time, + dependson => \&_check_dependency, + blocked => \&_check_dependency, + plans => \&_check_plans, + runs => \&_check_runs, + tags => \&_check_tags, + components => \&_check_components, + bugs => \&_check_bugs, + category_id => \&_check_category, +}; + +use constant ALIAS_MAX_LENGTH => 255; +use constant REQUIREMENT_MAX_LENGTH => 255; +use constant SUMMARY_MAX_LENGTH => 255; +use constant TAG_MAX_LENGTH => 255; + +sub display_columns { + my $self = shift; + my @columns = + [{column => 'case_id', desc => 'ID' }, + {column => 'case_status_id', desc => 'Status' }, + {column => 'category_id', desc => 'Category' }, + {column => 'priority_id', desc => 'Priority' }, + {column => 'summary', desc => 'Summary' }, + {column => 'requirement', desc => 'Requirement' }, + {column => 'alias', desc => 'Alias' }]; + + $self->{'display_columns'} = \@columns; + return $self->{'display_columns'}; +} + +sub report_columns { + my $self = shift; + my %columns; + # Changes here need to match Report.pm + $columns{'Status'} = "case_status"; + $columns{'Priority'} = "priority"; + $columns{'Product'} = "product"; + $columns{'Component'} = "component"; + $columns{'Category'} = "category"; + $columns{'Automated'} = "isautomated"; + $columns{'Tags'} = "tags"; + $columns{'Requirement'} = "requirement"; + $columns{'Author'} = "author"; + $columns{'Default tester'} = "default_tester"; + my @result; + push @result, {'name' => $_, 'id' => $columns{$_}} foreach (sort(keys %columns)); + unshift @result, {'name' => '', 'id'=> ''}; + return \@result; + +} + +############################### +#### Validators #### +############################### + +sub _check_status{ + my ($invocant, $status) = @_; + $status = trim($status); + my $status_id; + if ($status =~ /^\d+$/){ + $status_id = Bugzilla::Testopia::Util::validate_selection($status, 'case_status_id', 'test_case_status'); + } + else { + trick_taint($status); + $status_id = lookup_status_by_name($status); + } + ThrowUserError('invalid_status') unless $status_id; + return $status_id; +} + +sub _check_category{ + my ($invocant, $category, $product) = @_; + $category = trim($category); + my $category_id; + if (ref $category){ + $product = Bugzilla::Product::check_product($category->{'product'}); + $category_id = Bugzilla::Testopia::Category::check_case_category($category->{'category'}, $product); + } + elsif ($category =~ /^\d+$/){ + $category_id = Bugzilla::Testopia::Util::validate_selection($category, 'category_id', 'test_case_categories'); + } + else { + $category_id = Bugzilla::Testopia::Category::check_case_category($category, $product); + } + + return $category_id; +} + +sub _check_priority{ + my ($invocant, $priority) = @_; + $priority = trim($priority); + trick_taint($priority); + my $priority_id; + if ($priority =~ /^\d+$/){ + $priority_id = Bugzilla::Testopia::Util::validate_selection($priority, 'id', 'priority'); + } + else { + $priority_id = lookup_priority_by_value($priority); + } + ThrowCodeError('bad_arg', {argument => 'priority', function => 'set_priority'}) unless $priority_id; + return $priority_id; + +} + +sub _check_tester{ + my ($invocant, $tester) = @_; + $tester = trim($tester); + return unless $tester; + if ($tester =~ /^\d+$/){ + $tester = Bugzilla::User->new($tester); + return $tester->id; + } + else { + my $id = login_to_id($tester, THROW_ERROR); + return $id; + } +} + +sub _check_author{ + my ($invocant, $tester) = @_; + $tester = trim($tester); + return unless $tester; + if ($tester =~ /^\d+$/){ + $tester = Bugzilla::User->new($tester); + return $tester->id; + } + else { + my $id = login_to_id($tester, THROW_ERROR); + return $id; + } +} + +sub _check_automated{ + my ($invocant, $isactive) = @_; + $isactive = trim($isactive); + ThrowCodeError('bad_arg', {argument => 'isautomated', function => 'set_automated'}) unless ($isactive =~ /(1|0)/); + return $isactive; +} + +sub _check_sortkey{ + my ($invocant, $sortkey) = @_; + $sortkey = trim($sortkey); + return unless $sortkey; + ThrowCodeError('bad_arg', {argument => 'sortkey', function => 'set_sortkey'}) unless ($sortkey =~ /^\d+$/); + return $sortkey; +} + +sub _check_script{ + my ($invocant, $value) = @_; + return $value; +} + +sub _check_arguments{ + my ($invocant, $value) = @_; + return $value; +} + +sub _check_summary{ + my ($invocant, $summary) = @_; + $summary = clean_text($summary) if $summary; + + if (!defined $summary || $summary eq '') { + ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}); + } + return $summary; +} + +sub _check_requirements{ + my ($invocant, $value) = @_; + return $value; +} + +sub _check_alias { + my ($invocant, $alias) = @_; + $alias = trim($alias); + return unless $alias; + trick_taint($alias); + my $id; + my $dbh = Bugzilla->dbh; + + my $query = "SELECT case_id + FROM test_cases + WHERE alias = ?"; + + # If this is a new test case then we only need to check if another test case + # has this alias already. If we are updating the test case though, we know + # there is already at least one that has this alias - this case. + $query .= " AND case_id != ?" if ref $invocant; + + if (ref $invocant){ + ($id) = $dbh->selectrow_array($query, undef, ($alias, $invocant->id)); + } + else { + ($id) = $dbh->selectrow_array($query, undef, $alias); + } + ThrowUserError('testiopia-alias-exists', {'alias' => $alias}) if $id; + return $alias; +} + +sub _check_time{ + my ($invocant, $time) = @_; + $time = trim($time); + return '0:0:0' unless $time; + $time =~ m/^(\d+)[:\s](\d+)[:\s](\d+)$/; + ThrowUserError('testopia-format-error', {'field' => 'Estimated Time' }) + unless (defined $1 && defined $2 && $2 < 60 && defined $3 && $3 < 60); + $time = "$1:$2:$3"; + return $time; +} + +sub _check_dependency{ + my ($invocant, $value) = @_; + $value = trim($value); + if ($value) { + my @validvalues; + foreach my $id (split(/[\s,]+/, $value)) { + next unless $id; + Bugzilla::Testopia::Util::validate_test_id($id, 'case'); + push(@validvalues, $id); + } + $value = join(",", @validvalues); + return $value; + } + return; +} + +sub _check_plans { + my ($invocant, $plans) = @_; + # $plans is a reference to an array of Testopia::TestPlan objects. + ThrowUserError('plan_needed') unless scalar @$plans > 0; + return $plans; +} + +sub _check_cases { + my ($invocant, $caseids) = @_; + my @cases; + + foreach my $caseid (split(/[\s,]+/, $caseids)){ + Bugzilla::Testopia::Util::validate_test_id($caseid, 'case'); + push @cases, Bugzilla::Testopia::TestCase->new($caseid); + } + + return \@cases; +} + +sub _check_runs { + my ($invocant, $runids) = @_; + my @runs; + if (ref $runids eq 'ARRAY'){ + $runids = join(',' ,@$runids); + } + foreach my $runid (split(/[\s,]+/, $runids)){ + Bugzilla::Testopia::Util::validate_test_id($runid, 'run'); + push @runs, Bugzilla::Testopia::TestRun->new($runid); + } + return \@runs; +} + +sub _check_tags { + my ($invocant, $tags) = @_; + + return $tags; +} + +sub _check_components { + my ($invocant, $components) = @_; + my @components; + my @comp_ids; + my $dbh = Bugzilla->dbh; + + ThrowUserError('testopia-missing-parameter', {param => 'components'}) unless $components; + if (ref $components eq 'HASH'){ + my $prod = Bugzilla::Product::check_product($components->{'product'}); + my $comp = Bugzilla::Component->check({product=> $prod, name => $components->{'component'}}); + push @comp_ids, $comp->id; + } + elsif (ref $components eq 'ARRAY'){ + foreach my $c (@$components){ + if (ref $c){ + my $prod = Bugzilla::Product::check_product($c->{'product'}); + my $comp = Bugzilla::Component->check({product => $prod, name => $c->{'component'}}); + push @comp_ids, $comp->id; + } + else{ + validate_selection($c,'id','components'); + push @comp_ids, $c; + } + } + } + else { + @comp_ids = split(/[\s,]+/, $components); + } + foreach my $id (@comp_ids){ + Bugzilla::Testopia::Util::validate_selection($id, 'id', 'components'); + trick_taint($id); + + if (ref $invocant){ + my ($is) = $dbh->selectrow_array( + "SELECT case_id FROM test_case_components + WHERE case_id = ? AND component_id = ?", + undef, ($invocant->id, $id)); + ThrowUserError('testopia_component_attached') if $is; + } + + push @components, $id; + } + return \@components; +} + +sub _check_bugs { + my ($invocant, $bugids, $attach) = @_; + my @bugids; + my @ids; + my $dbh = Bugzilla->dbh; + + if (ref $bugids eq 'ARRAY'){ + push @ids, @$bugids; + } + else { + push @ids, split(/[\s,]+/, $bugids); + } + + foreach my $bug (@ids){ + trick_taint($bug); + Bugzilla::Bug::ValidateBugID($bug); + if (ref $invocant && $attach){ + my ($exists) = $dbh->selectrow_array( + "SELECT bug_id + FROM test_case_bugs + WHERE case_id=? + AND bug_id=?", + undef, ($invocant->id, $bug)); + next if ($exists); + } + push @bugids, $bug; + } + + return \@bugids; +} + +############################### +#### Mutators #### +############################### +sub set_case_status { $_[0]->set('case_status_id', $_[1]); } +sub set_category { $_[0]->set('category_id', $_[1]); } +sub set_priority { $_[0]->set('priority_id', $_[1]); } +sub set_default_tester { $_[0]->set('default_tester_id', $_[1]); } +sub set_sortkey { $_[0]->set('sortkey', $_[1]); } +sub set_requirement { $_[0]->set('requirement', $_[1]); } +sub set_isautomated { $_[0]->set('isautomated', $_[1]); } +sub set_script { $_[0]->set('script', $_[1]); } +sub set_arguments { $_[0]->set('arguments', $_[1]); } +sub set_summary { $_[0]->set('summary', $_[1]); } +sub set_alias { $_[0]->set('alias', $_[1]); } +sub set_estimated_time { $_[0]->set('estimated_time', $_[1]); } +sub set_dependson { $_[0]->set('dependson', $_[1]); } +sub set_blocks { $_[0]->set('blocks', $_[1]); } + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + bless($param, $class); + return $param; + } + if (!defined $param || (!ref($param) && $param !~ /^\d+$/)) { + $param = { name => $param }; + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $product = $params->{plans}->[0]->product; + + $params->{category_id} = $class->_check_category($params->{category_id}, $product); + + return $params; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{creation_date} = Bugzilla::Testopia::Util::get_time_stamp(); + + # We have to handle these fields a bit differently since they have their own tables. + my $action = $field_values->{action}; + my $effect = $field_values->{effect}; + my $setup = $field_values->{setup}; + my $breakdown = $field_values->{breakdown}; + my $dependson = $field_values->{dependson}; + my $blocks = $field_values->{blocks}; + my $plans = $field_values->{plans}; + my $runs = $field_values->{runs}; + my $component = $field_values->{components}; + my $tags = $field_values->{tags}; + my $bugs = $field_values->{bugs}; + + # Since these are not part of a case strictly speaking, we remove them before + # calling insert_create_data. + foreach my $field (qw(action effect setup breakdown dependson blocks plans runs components tags bugs)){ + delete $field_values->{$field}; + } + + my $self = $class->SUPER::insert_create_data($field_values); + + $self->store_text($self->id, $field_values->{'author_id'}, $action, $effect, + $setup, $breakdown,0); + + $self->update_deps($dependson, $blocks, $self->id); + + foreach my $p (@$plans){ + $self->link_plan($p->id, $self->id); + } + delete $self->{'plans'}; + + foreach my $run (@$runs){ + $run->add_case_run($self->id, $self->sortkey); + } + + $self->add_component($component, "VALIDATED"); + $self->add_tag($tags); + $self->attach_bug($bugs); + + return $self; +} + +sub update { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + $self->update_deps($self->{'dependson'}, $self->{'blocks'}); + + $dbh->bz_start_transaction(); + + my $changed = $self->SUPER::update(); + + delete $changed->{'sortkey'}; + + foreach my $field (keys %$changed){ + Bugzilla::Testopia::Util::log_activity('case', $self->id, $field, $timestamp, + $changed->{$field}->[0], $changed->{$field}->[1]); + } + + $dbh->bz_commit_transaction(); +} + +############################### +#### Functions #### +############################### + +sub lookup_status { + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM test_case_status + WHERE case_status_id = ?", + undef, $id); + return $value; +} + +sub lookup_status_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT case_status_id + FROM test_case_status + WHERE name = ?", + undef, $name); + return $value; +} + +sub lookup_category { + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM test_case_categories + WHERE category_id = ?", + undef, $id); + return $value; +} + +sub lookup_category_by_name { + my ($name) = @_; + trick_taint $name; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT category_id + FROM test_case_categories + WHERE name = ?", + undef, $name); + return $value; +} + +sub lookup_priority { + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT value + FROM priority + WHERE id = ?", + undef, $id); + return $value; +} + +sub lookup_priority_by_value { + my ($value) = @_; + my $dbh = Bugzilla->dbh; + my ($id) = $dbh->selectrow_array( + "SELECT id + FROM priority + WHERE value = ?", + undef, $value); + return $id; +} + +sub lookup_default_tester { + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT login_name + FROM profiles + WHERE userid = ?", + undef, $id); + return $value; +} + +############################### +#### Methods #### +############################### +sub get_selectable_components { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my @exclusions; + unless ($byid) { + foreach my $e (@{$self->components}){ + push @exclusions, $e->{'id'}; + } + } + my $query = "SELECT id FROM components + WHERE product_id IN (" . join(",", @{$self->get_product_ids}) . ") "; + if (@exclusions){ + $query .= "AND id NOT IN(". join(",", @exclusions) .") "; + } + $query .= "ORDER BY name"; + + my $ref = $dbh->selectcol_arrayref($query); + my @comps; + push @comps, {'id' => '0', 'name' => '--Please Select--'} unless $byid; + foreach my $id (@$ref){ + push @comps, Bugzilla::Component->new($id); + } + return \@comps; +} + +=head2 get_category_list + +Returns a list of categories associated with products in all +plans referenced by this case. + +=cut + +sub get_category_list{ + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT category_id + FROM test_case_categories + WHERE product_id IN (". join(",", @{$self->get_product_ids}) .")"); + my @categories; + foreach my $c (@$ids){ + push @categories, Bugzilla::Testopia::Category->new($c); + } + return \@categories; +} + +=head2 get_product_ids + +Returns the list of product ids that this case is associated with + +=cut + +sub get_product_ids { + my $self = shift; + + if ($self->id == 0){ + my @ids; + foreach my $plan (@{$self->plans}){ + push @ids, $plan->product_id if Bugzilla->user->can_see_product($plan->product->name); + } + return \@ids; + } + + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT products.id FROM products + JOIN test_plans AS plans ON plans.product_id = products.id + JOIN test_case_plans ON plans.plan_id = test_case_plans.plan_id + JOIN test_cases AS cases ON cases.case_id = test_case_plans.case_id + WHERE cases.case_id = ? + ORDER BY products.id", undef, $self->{'case_id'}); + return $ref; +} + +=head2 get_status_list + +Returns the list of legal statuses for a test case + +=cut + +sub get_status_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref(" + SELECT case_status_id AS id, name + FROM test_case_status", {"Slice"=>{}}); + return $ref +} + +=head2 get_text_versions + +Returns the list of versions of the plan document. + +=cut + +sub get_text_versions { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $versions = $dbh->selectall_arrayref( + "SELECT case_text_version AS id, case_text_version AS name + FROM test_case_texts + WHERE case_id = ? + ORDER BY case_text_version", + {'Slice' =>{}}, $self->id); + return $versions; +} + +=head2 get_priority_list + +Returns a list of legal priorities + +=cut + +sub get_priority_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref(" + SELECT id, value AS name FROM priority + ORDER BY sortkey", {"Slice"=>{}}); + return $ref +} + +=head2 get_caserun_count + +Takes a status and returns the count of that status + +=cut + +sub get_caserun_count { + my $self = shift; + my ($status) = @_; + my $dbh = Bugzilla->dbh; + + my $query = "SELECT COUNT(*) + FROM test_case_runs + WHERE case_id = ? "; + $query .= "AND case_run_status_id = ?" if $status; + + my $count; + if ($status){ + ($count) = $dbh->selectrow_array($query,undef,($self->id,$status)); + } + else { + ($count) = $dbh->selectrow_array($query,undef,$self->id); + } + return $count; +} + +=head2 add_tag + +Associates a tag with this test case + +=cut + +sub add_tag { + my $self = shift; + my $dbh = Bugzilla->dbh; + my @tags; + foreach my $t (@_){ + if (ref $t eq 'ARRAY'){ + push @tags, $_ foreach @$t; + } + else { + push @tags, split(/,+/, $t); + } + } + + foreach my $name (@tags){ + my $tag = Bugzilla::Testopia::TestTag->create({'tag_name' => $name}); + $tag->attach($self); + } +} + +=head2 remove_tag + +Disassociates a tag from this test case + +=cut + +sub remove_tag { + my $self = shift; + my ($tag_name) = @_; + my $tag = Bugzilla::Testopia::TestTag->check_tag($tag_name); + ThrowUserError('testopia-unknown-tag', {'name' => $tag}) unless $tag; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_case_tags + WHERE tag_id=? AND case_id=?", + undef, $tag->id, $self->{'case_id'}); + return; +} + +=head2 attach_bug + +Attaches the specified bug to this test case + +=cut + +sub attach_bug { + my $self = shift; + my ($bugids, $caserun_id) = @_; + my $dbh = Bugzilla->dbh; + trick_taint($caserun_id) if $caserun_id; + + $bugids = $self->_check_bugs($bugids, "ATTACH"); + + $dbh->bz_start_transaction(); + + foreach my $bug (@$bugids){ + $dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id, case_id) + VALUES(?,?,?)", undef, ($bug, $caserun_id, $self->id)); + } + + $dbh->bz_commit_transaction(); +} + +=head2 detach_bug + +Removes the association of the specified bug from this test case-run + +=cut + +sub detach_bug { + my $self = shift; + my ($bugids) = @_; + my $dbh = Bugzilla->dbh; + + $bugids = $self->_check_bugs($bugids); + + foreach my $bug (@$bugids){ + $dbh->do("DELETE FROM test_case_bugs + WHERE bug_id = ? + AND case_id = ?", + undef, ($bug, $self->{'case_id'})); + } +} + +=head2 add_component + +Associates a component with this test case + +=cut + +sub add_component { + my $self = shift; + my ($compids, $validated) = @_; + my $dbh = Bugzilla->dbh; + my $comps = $validated ? $compids : $self->_check_components($compids); + foreach my $id (@$comps){ + $dbh->do("INSERT INTO test_case_components (case_id, component_id) + VALUES (?,?)",undef, $self->{'case_id'}, $id); + } + + delete $self->{'components'}; +} + +sub add_to_run { + my $self = shift; + my ($runids) = @_; + my $runs = $self->_check_runs($runids); + + foreach my $run (@$runs){ + $run->add_case_run($self->id, $self->sortkey); + } +} + +=head2 add_blocks + +Adds a list of test cases to the current test cases being blocked by the testcase + +=cut + +sub add_blocks { + my $self = shift; + my $case_ids = shift; + + my $cases = $self->_check_cases($case_ids); + my @blocks; + + my $dbh = Bugzilla->dbh(); + + # Get a list of the cases this test case currently blocks + my $current_blocks = $dbh->selectcol_arrayref("SELECT blocked + FROM test_case_dependencies + WHERE dependson = ?", + undef, + $self->id()); + + # update the list of items + foreach (@$cases) { + push @blocks, $_->id(); + } + + push @blocks, @$current_blocks; + + $cases = join ",", @blocks; + + $self->set_blocks($cases); + $self->update(); +} + +=head2 remove_blocks + +Removes a list of test cases from being blocked by the testcase + +=cut + +sub remove_blocks { + my $self = shift; + my $case_ids = shift; + + return 0 if ($case_ids eq '' or !defined $case_ids); + + my $dbh = Bugzilla->dbh(); + + my @cases; + + foreach my $case (split /[\s,]+/, $case_ids){ + detaint_natural($case); + push @cases, $case; + } + + my $query ="DELETE + FROM test_case_dependencies + WHERE dependson = ?"; + + $query .= "AND ("; + $query .= join(" or ", map {"blocked = ?"} @cases); + $query .= ")"; + + $dbh->do($query, undef, $self->id, @cases); +} + +=head2 add_dependson + +Adds a list of test cases to the current test cases depended on by the testcase + +=cut + +sub add_dependson { + my $self = shift; + my $case_ids = shift; + + my $cases = $self->_check_cases($case_ids); + my @dependson; + + my $dbh = Bugzilla->dbh(); + + # Get a list of the cases this test case currently blocks + my $current_dependson = $dbh->selectcol_arrayref("SELECT dependson + FROM test_case_dependencies + WHERE blocked = ?", + undef, + $self->id()); + + # update the list of items + foreach (@$cases) { + push @dependson, $_->id(); + } + + push @dependson, @$current_dependson; + + $cases = join ",", @dependson; + + $self->set_dependson($cases); + $self->update(); +} + +=head2 remove_dependson + +Adds a list of test cases to the current test cases depended on by the testcase + +=cut + +sub remove_dependson { + my $self = shift; + my $case_ids = shift; + + return 0 if ($case_ids eq '' or !defined $case_ids); + + my $dbh = Bugzilla->dbh(); + + my @cases; + + foreach my $case (split /[\s,]+/, $case_ids) + { + detaint_natural($case); + push @cases, $case; + } + + my $query = " + DELETE + FROM test_case_dependencies + WHERE blocked = ?"; + + + $query .= "AND ("; + $query .= join(" or ", map {"dependson = ?"} @cases); + $query .= ")"; + + $dbh->do($query, undef, $self->id, @cases); +} + +=head2 remove_component + +Disassociates a component with this test case + +=cut + +sub remove_component { + my $self = shift; + my ($comp_id) = @_; + trick_taint($comp_id); + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_case_components + WHERE case_id = ? AND component_id = ?", + undef, $self->{'case_id'}, $comp_id); +} + +=head2 compare_doc_versions + +Returns a unified diff of two versions of a case document (action +and effect). It takes two arguments both integers representing +the first and second versions to compare. + +=cut + +sub compare_doc_versions { + my $self = shift; + my ($newversion, $oldversion) = @_; + detaint_natural($newversion); + detaint_natural($oldversion); + my $dbh = Bugzilla->dbh; + my %diff; + my ($newaction, $neweffect, $newsetup, $newbreakdown) = $dbh->selectrow_array( + "SELECT action, effect, setup, breakdown FROM test_case_texts + WHERE case_id = ? AND case_text_version = ?", + undef, $self->{'case_id'}, $newversion); + + my ($oldaction, $oldeffect, $oldsetup, $oldbreakdown) = $dbh->selectrow_array( + "SELECT action, effect, setup, breakdown FROM test_case_texts + WHERE case_id = ? AND case_text_version = ?", + undef, $self->{'case_id'}, $oldversion); + $diff{'action'} = diff(\$newaction, \$oldaction); + $diff{'effect'} = diff(\$neweffect, \$oldeffect); + $diff{'setup'} = diff(\$newsetup, \$oldsetup); + $diff{'breakdown'} = diff(\$newbreakdown, \$oldbreakdown); + return \%diff; +} + +=head2 diff_case_doc + +Returns the diff of the latest case document (action and effect) +with some text passed as an argument. Used to determine if the +text has changed and thus requiring a new version be created. + +=cut + +sub diff_case_doc { + my $self = shift; + my ($newaction, $neweffect, $newsetup, $newbreakdown) = @_; + my $dbh = Bugzilla->dbh; + my ($oldaction, $oldeffect, $oldsetup, $oldbreakdown) = $dbh->selectrow_array( + "SELECT action, effect, setup, breakdown + FROM test_case_texts + WHERE case_id = ? AND case_text_version = ?", + undef, ($self->{'case_id'}, $self->version)); + my $diff = diff(\$newaction, \$oldaction); + $diff .= diff(\$neweffect, \$oldeffect); + $diff .= diff(\$newsetup, \$oldsetup); + $diff .= diff(\$newbreakdown, \$oldbreakdown); + return $diff +} + +=head2 get_fields + +Returns a reference to a list of test case field descriptions from +the test_fielddefs table. + +=cut + +sub get_fields { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $types = $dbh->selectall_arrayref( + "SELECT fieldid AS id, description AS name + FROM test_fielddefs + WHERE table_name=?", + {"Slice"=>{}}, "test_cases"); + unshift @$types, {id => 'text', name => 'Text (Action, Setup, etc.)'}; + unshift @$types, {id => '[Creation]', name => '[Created]'}; + return $types; +} + +=head2 store + +Stores a test case object in the database. This method is used to store a +newly created test case. It returns the new ID. + +=cut + +sub store { + my $self = shift; + my $dbh = Bugzilla->dbh; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'case_id'} DB_COLUMNS); + my ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); + + $dbh->do("INSERT INTO test_cases ($columns) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + undef, ## Database Column ## + ($self->{'case_status_id'}, # case_status_id + $self->{'category_id'}, # category_id + $self->{'priority_id'}, # priority_id + $self->{'author_id'}, # author_id + $self->{'default_tester_id'}, # default_tester_id + $timestamp, # creation_date + $self->{'estimated_time'}, # estimated_time + $self->{'isautomated'}, # isautomated + $self->sortkey, # sortkey + $self->{'script'}, # script + $self->{'arguments'}, # arguments + $self->{'summary'}, # summary + $self->{'requirement'}, # requirement + $self->{'alias'}, # alias + )); + my $key = $dbh->bz_last_key( 'test_cases', 'case_id' ); + + $self->store_text($key, $self->{'author_id'}, $self->{'action'}, $self->{'effect'}, + $self->{'setup'}, $self->{'breakdown'},0 ,$timestamp); + $self->update_deps($self->{'dependson'}, $self->{'blocks'}, $key); + foreach my $p (@{$self->{'plans'}}){ + $self->link_plan($p->id, $key); + } + return $key; +} + +=head2 store_text + +Stores the test case document (action and effect) in the test_case_texts +table. Used by both store and copy. Accepts the the test case id, +author id, action text, effect text, and an optional timestamp. + +=cut + +sub store_text { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($key, $author, $action, $effect, $setup, $breakdown, $reset_version, $timestamp) = @_; + if (!defined $timestamp){ + ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); + } + trick_taint($action) if $action; + trick_taint($effect) if $effect; + trick_taint($breakdown) if $breakdown; + trick_taint($setup) if $setup; + + my $version = $reset_version ? 0 : $self->version || 0; + $dbh->do("INSERT INTO test_case_texts + (case_id, case_text_version, who, creation_ts, action, effect, setup, breakdown) + VALUES(?,?,?,?,?,?,?,?)", + undef, $key, ++$version, $author, + $timestamp, $action, $effect, $setup, $breakdown); + $self->{'version'} = $version; + return $self->{'version'}; + +} + +=head2 link_plan + +Creates a link to the specified plan id. Optionally can create a link +for an arbitrary test case, not just this one. + +=cut + +sub link_plan { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($plan_id, $case_id) = @_; + $case_id = $self->{'case_id'} unless defined $case_id; + + #Check that it isn't linked already + + $dbh->bz_start_transaction(); + my ($is) = $dbh->selectrow_array( + "SELECT 1 + FROM test_case_plans + WHERE case_id = ? + AND plan_id = ?", + undef, ($case_id, $plan_id)); + if ($is) { + $dbh->bz_commit_transaction(); + return; + } + $dbh->do("INSERT INTO test_case_plans (plan_id, case_id) + VALUES (?,?)", undef, $plan_id, $case_id); + $dbh->bz_commit_transaction(); + + # Update the plans array to include new plan added. + + push @{$self->{'plans'}}, Bugzilla::Testopia::TestPlan->new($plan_id); + +} + + +=head2 unlink_plan + +Removes the link to the specified plan id. + +=cut + +sub unlink_plan { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($plan_id) = @_; + my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + + if (scalar @{$self->plans} == 1){ + return 0; #Return failure + } + + $dbh->bz_start_transaction(); + + foreach my $run (@{$plan->test_runs}){ + $dbh->do("DELETE FROM test_case_runs + WHERE case_id = ? + AND run_id = ?", undef, $self->id, $run->id); + } + + $dbh->do("DELETE FROM test_case_plans + WHERE plan_id = ? + AND case_id = ?", + undef, $plan_id, $self->{'case_id'}); + + $dbh->bz_commit_transaction(); + + # Update the plans array. + delete $self->{'plans'}; + + return 1; +} + +=head2 copy + +Creates a copy of this test case. Accepts the plan id to link to and +a boolean representing whether to copy the case document as well. + +=cut + +sub copy { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($author, $tester, $copydoc, $category_id) = @_; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'case_id'} DB_COLUMNS); + my ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); + $category_id ||= $self->{'category_id'}; + + $dbh->do("INSERT INTO test_cases ($columns) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + undef, + ($self->{'case_status_id'}, # case_status_id + $category_id, # category_id + $self->{'priority_id'}, # priority_id + $author, # author_id + $tester, # default_tester_id + $timestamp, # creation_date + $self->{'estimated_time'}, # estimated_time + $self->{'isautomated'}, # isautomated + $self->sortkey, # sortkey + $self->{'script'}, # script + $self->{'arguments'}, # arguments + $self->{'summary'}, # summary + $self->{'requirement'}, # requirement + undef # alias + )); + + my $key = $dbh->bz_last_key( 'test_cases', 'case_id' ); + + if ($copydoc){ + $self->store_text($key, Bugzilla->user->id, $self->text->{'action'}, + $self->text->{'effect'}, $self->text->{'setup'}, + $self->text->{'breakdown'},'VRESET' , $timestamp); + } + else{ + $self->store_text($key, Bugzilla->user->id, '', '', '', '', 'VRESET', $timestamp); + } + return $key; + +} + +=head2 check_alias + +Checks if the given alias exists already. Returns the case_id of +the matching case if it does. + +=cut + + + +=head2 class_check_alias + +Checks if the given alias exists already. Returns the case_id of +the matching test case if it does. + +=cut + +sub class_check_alias { + my ($alias) = @_; + + return unless $alias; + + my $dbh = Bugzilla->dbh; + my ($id) = $dbh->selectrow_array( + "SELECT case_id + FROM test_cases + WHERE alias = ?", + undef, ($alias)); + + return $id; +} + +=head2 update + +Updates this test case with new values supplied by the user. +Accepts a reference to a hash with keys identical to a test cases +fields and values representing the new values entered. +Validation tests should be performed on the values +before calling this method. If a field is changed, a history +of that change is logged in the test_case_activity table. + +=cut + + +=head2 history + +Returns a reference to a list of history entries from the +test_case_activity table. + +=cut + +sub history { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT defs.description AS what, + p.login_name AS who, a.changed, a.oldvalue, a.newvalue + FROM test_case_activity AS a + JOIN test_fielddefs AS defs ON a.fieldid = defs.fieldid + JOIN profiles AS p ON a.who = p.userid + WHERE a.case_id = ?", + {'Slice'=>{}}, $self->{'case_id'}); + + foreach my $row (@$ref){ + if ($row->{'what'} eq 'Case Status'){ + $row->{'oldvalue'} = lookup_status($row->{'oldvalue'}); + $row->{'newvalue'} = lookup_status($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Category'){ + $row->{'oldvalue'} = lookup_category($row->{'oldvalue'}); + $row->{'newvalue'} = lookup_category($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Priority'){ + $row->{'oldvalue'} = lookup_priority($row->{'oldvalue'}); + $row->{'newvalue'} = lookup_priority($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Default Tester'){ + $row->{'oldvalue'} = lookup_default_tester($row->{'oldvalue'}); + $row->{'newvalue'} = lookup_default_tester($row->{'newvalue'}); + } + } + + return $ref; +} + +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_case_activity + WHERE case_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; + +} + + +# From process bug +sub _snap_shot_deps { + my ($i, $target, $me) = (@_); + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectcol_arrayref( + "SELECT $target + FROM test_case_dependencies + WHERE $me = ? ORDER BY $target", undef, $i); + + return join(',', @{$ref}); +} + +# Taken from Bugzilla::Bug +sub _get_dep_lists { + my ($myfield, $targetfield, $case_id) = (@_); + my $dbh = Bugzilla->dbh; + my $list_ref = + $dbh->selectcol_arrayref( + "SELECT test_case_dependencies.$targetfield + FROM test_case_dependencies + JOIN test_cases ON test_cases.case_id = test_case_dependencies.$targetfield + WHERE test_case_dependencies.$myfield = ? + ORDER BY test_case_dependencies.$targetfield", + undef, ($case_id)); + return $list_ref; +} + +sub update_deps { + my $self = shift; + my ($dependson, $blocks, $case_id) = @_; + $case_id = $self->{'case_id'} unless $case_id; + my $dbh = Bugzilla->dbh; + my $fields = {}; + $fields->{'dependson'} = $dependson; + $fields->{'blocked'} = $blocks; + # From process bug + foreach my $field ("dependson", "blocked") { + if (exists $fields->{$field}) { + my @validvalues; + foreach my $id (split(/[\s,]+/, $fields->{$field})) { + next unless $id; + Bugzilla::Testopia::Util::validate_test_id($id, 'case'); + push(@validvalues, $id); + } + $fields->{$field} = join(",", @validvalues); + } + } + #From Bug.pm sub ValidateDependencies($$$) + my $id = $case_id || 0; + + unless (defined($fields->{'dependson'}) + || defined($fields->{'blocked'})) + { + return; + } + + my %deps; + my %deptree; + foreach my $pair (["blocked", "dependson"], ["dependson", "blocked"]) { + my ($me, $target) = @{$pair}; + $deptree{$target} = []; + $deps{$target} = []; + next unless $fields->{$target}; + + my %seen; + foreach my $i (split('[\s,]+', $fields->{$target})) { + if ($id == $i) { + ThrowUserError("dependency_loop_single"); + } + if (!exists $seen{$i}) { + push(@{$deptree{$target}}, $i); + $seen{$i} = 1; + } + } + # populate $deps{$target} as first-level deps only. + # and find remainder of dependency tree in $deptree{$target} + @{$deps{$target}} = @{$deptree{$target}}; + my @stack = @{$deps{$target}}; + while (@stack) { + my $i = shift @stack; + my $dep_list = + $dbh->selectcol_arrayref("SELECT $target + FROM test_case_dependencies + WHERE $me = ?", undef, $i); + foreach my $t (@$dep_list) { + # ignore any _current_ dependencies involving this test_case, + # as they will be overwritten with data from the form. + if ($t != $id && !exists $seen{$t}) { + push(@{$deptree{$target}}, $t); + push @stack, $t; + $seen{$t} = 1; + } + } + } + } + + my @deps = @{$deptree{'dependson'}}; + my @blocks = @{$deptree{'blocked'}}; + my @union = (); + my @isect = (); + my %union = (); + my %isect = (); + foreach my $block (@deps, @blocks) { $union{$block}++ && $isect{$block}++ } + @union = keys %union; + @isect = keys %isect; + if (scalar(@isect) > 0) { + my $both = ""; + foreach my $i (@isect) { + $both .= "$i " ; + } + ThrowUserError("dependency_loop_multi", { both => $both }); + } + + #from process_bug + foreach my $pair ("blocked/dependson", "dependson/blocked") { + my ($me, $target) = split("/", $pair); + + my @oldlist = @{$dbh->selectcol_arrayref("SELECT $target FROM test_case_dependencies + WHERE $me = ? ORDER BY $target", + undef, $id)}; + + if (defined $fields->{$target}) { + my %snapshot; + my @newlist = sort {$a <=> $b} @{$deps{$target}}; + + while (0 < @oldlist || 0 < @newlist) { + if (@oldlist == 0 || (@newlist > 0 && + $oldlist[0] > $newlist[0])) { + $snapshot{$newlist[0]} = _snap_shot_deps($newlist[0], $me, + $target); + shift @newlist; + } elsif (@newlist == 0 || (@oldlist > 0 && + $newlist[0] > $oldlist[0])) { + $snapshot{$oldlist[0]} = _snap_shot_deps($oldlist[0], $me, + $target); + shift @oldlist; + } else { + if ($oldlist[0] != $newlist[0]) { + die "Error in list comparing code"; + } + shift @oldlist; + shift @newlist; + } + } + my @keys = keys(%snapshot); + if (@keys) { + my $oldsnap = _snap_shot_deps($id, $target, $me); + $dbh->do("DELETE FROM test_case_dependencies WHERE $me = ?", undef, $id); + foreach my $i (@{$deps{$target}}) { + $dbh->do("INSERT INTO test_case_dependencies ($me, $target) VALUES (?,?)", undef, $id, $i); + } + } + } + } + delete $self->{'blocked'}; + delete $self->{'blocks'}; + delete $self->{'dependson'}; + delete $self->{'blocked_list'}; + delete $self->{'dependson_list'}; +} + +=head2 get_dep_tree + +Returns a list of test case dependencies + +=cut + +sub get_dep_tree { + my $self = shift; + $self->{'dep_list'} = (); + $self->_generate_dep_tree($self->{'case_id'}); + return $self->{'dep_list'}; +} + +=head2 _generate_dep_tree + +Private method that recursivly gets a list of the test cases this blocks + +=cut + +sub _generate_dep_tree { + my $self = shift; + my ($case_id) = @_; + my $deps = _get_dep_lists("dependson", "blocked", $case_id); + return unless scalar @$deps; + foreach my $id (@$deps){ + $self->_generate_dep_tree($id); + push @{$self->{'dep_list'}}, $id + } +} + +=head2 obliterate + +Removes this case and all things that reference it. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + + foreach my $obj (@{$self->attachments}){ + $obj->obliterate; + } + foreach my $obj (@{$self->caseruns}){ + $obj->obliterate; + } + + $dbh->do("DELETE FROM test_case_texts WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_plans WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_components WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_tags WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_bugs WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_activity WHERE case_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_dependencies + WHERE dependson = ? OR blocked = ?", undef, ($self->id, $self->id)); + $dbh->do("DELETE FROM test_cases WHERE case_id = ?", undef, $self->id); + return 1; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + + my @plan_ids; + foreach my $p (@{$self->plans}){ + push @plan_ids, $p->id; + } + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + $obj->{'run_count'} = $self->run_count; + $obj->{'author_name'} = $self->author->name if $self->author; + $obj->{'default_tester'} = $self->default_tester->name if $self->default_tester; + $obj->{'status'} = $self->status; + $obj->{'priority'} = $self->priority; + $obj->{'plan_id'} = $plan_ids[0]; + $obj->{'plan_ids'} = \@plan_ids; + $obj->{'type'} = $self->type; + $obj->{'id'} = $self->id; + $obj->{'canedit'} = $self->canedit; + $obj->{'canview'} = $self->canview; + $obj->{'candelete'} = $self->candelete; + $obj->{'category_name'} = $self->category->name if $self->category; + $obj->{'product_id'} = $self->plans->[0]->product_id if scalar @{$self->plans}; + $obj->{'blocked'} = $self->blocked_list; + $obj->{'dependson'} = $self->dependson_list; + $obj->{'component'} = $self->components->[0]->name if scalar @{$self->components}; + $obj->{'modified'} = $self->last_changed; + + return $json->encode($obj); +} + +=head2 canview + +Returns true if the logged in user has rights to view this test case. + +=cut + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->get_user_rights(Bugzilla->user->id) & TR_READ; + return 0; +} + +=head2 canedit + +Returns true if the logged in user has rights to edit this test case. + +=cut + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->get_user_rights(Bugzilla->user->id) & TR_WRITE; + return 0; +} + +=head2 candelete + +Returns true if the logged in user has rights to delete this test case. + +=cut + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 0 unless Bugzilla->params->{"allow-test-deletion"}; + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + # Otherwise, check for delete rights on all the plans this is linked to + my $own_all = 1; + foreach my $plan (@{$self->plans}){ + if (!($plan->get_user_rights(Bugzilla->user->id) & TR_DELETE)) { + $own_all = 0; + last; + } + } + return 1 if $own_all; + return 0; +} + +=head2 can_unlink_plan + +Returns true if this test case can be unlinked from the given plan + +=cut + +sub can_unlink_plan { + my $self = shift; + my ($plan_id) = @_; + + my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + return 1 if Bugzilla->user->in_group('admin'); + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + return 1 if $plan->get_user_rights(Bugzilla->user->id) & TR_DELETE; + return 0; +} + +sub get_user_rights { + my $self = shift; + my ($userid) = @_; + my $dbh = Bugzilla->dbh; + + my $plan_ids = $dbh->selectcol_arrayref( + "SELECT plan_id FROM test_case_plans WHERE case_id = ?", + undef, $self->id); + + return 0 unless $plan_ids; + $plan_ids = join(',',@$plan_ids); + + my ($perms) = $dbh->selectrow_array( + "SELECT MAX(permissions) FROM test_plan_permissions + LEFT JOIN test_case_plans ON test_plan_permissions.plan_id = test_case_plans.plan_id + INNER JOIN test_cases ON test_case_plans.case_id = test_cases.case_id + WHERE userid = ? AND test_plan_permissions.plan_id IN ($plan_ids)", + undef, $userid); + + return $perms; +} +############################### +#### Accessors #### +############################### + +=head1 ACCESSOR METHODS + +=head2 id + +Returns the ID of this object + +=head2 author + +Returns a Bugzilla::User object representing the Author of this case + +=head2 default_tester + +Returns a Bugzilla::User object representing the run's default tester + +=head2 creation_date + +Returns the creation time stamp of this object + +=head2 isautomated + +Returns true if this is an automatic test case + +=head2 script + +Returns the script of this object + +=head2 status_id + +Returns the status_id of this object + +=head2 arguments + +Returns the arguments for the script of this object + +=head2 summary + +Returns the summary of this object + +=head2 requirements + +Returns the requirements of this object + +=head2 alias + +Returns the alias of this object + +=cut + +sub id { return $_[0]->{'case_id'}; } +sub author { return Bugzilla::User->new($_[0]->{'author_id'}); } +sub default_tester { return Bugzilla::User->new($_[0]->{'default_tester_id'}); } +sub creation_date { return $_[0]->{'creation_date'}; } +sub estimated_time { return $_[0]->{'estimated_time'}; } +sub isautomated { return $_[0]->{'isautomated'}; } +sub script { return $_[0]->{'script'}; } +sub status_id { return $_[0]->{'case_status_id'};} +sub arguments { return $_[0]->{'arguments'}; } +sub summary { return $_[0]->{'summary'}; } +sub requirement { return $_[0]->{'requirement'}; } +sub alias { return $_[0]->{'alias'}; } + +=head2 type + +Returns 'case' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'case'; + return $self->{'type'}; +} + +=head2 attachments + +Returns a reference to a list of attachments associated with this +case. + +=cut + +sub attachments { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'attachments'} if exists $self->{'attachments'}; + + my $attachments = $dbh->selectcol_arrayref( + "SELECT attachment_id + FROM test_case_attachments + WHERE case_id = ?", + undef, $self->{'case_id'}); + + my @attachments; + foreach my $attach (@{$attachments}){ + push @attachments, Bugzilla::Testopia::Attachment->new($attach); + } + $self->{'attachments'} = \@attachments; + return $self->{'attachments'}; + +} + +=head2 version + +Returns the case text version. This number is incremented any time +changes are made to the case docs (action and effect). + +=cut + +sub version { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'version'} if exists $self->{'version'}; + my ($ver) = $dbh->selectrow_array("SELECT MAX(case_text_version) + FROM test_case_texts + WHERE case_id = ?", + undef, $self->{'case_id'}); + $self->{'version'} = $ver; + return $self->{'version'}; +} + +=head2 status + +Looks up the case status based on the case_status_id of this case. + +=cut + +sub status { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'status'} if exists $self->{'status'}; + my ($res) = $dbh->selectrow_array("SELECT name + FROM test_case_status + WHERE case_status_id = ?", + undef, $self->{'case_status_id'}); + $self->{'status'} = $res; + return $self->{'status'}; +} + +=head2 priority + +Looks up the Bugzilla priority value based on the priority_id +of this case. + +=cut + +sub priority { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'priority'} if exists $self->{'priority'}; + my ($res) = $dbh->selectrow_array("SELECT value + FROM priority + WHERE id = ?", + undef, $self->{'priority_id'}); + $self->{'priority'} = $res; + return $self->{'priority'}; +} + +=head2 category + +Returns the category name based on the category_id of this case + +=cut + +sub category { + my $self = shift; + return $self->{'category'} if exists $self->{'category'}; + $self->{'category'} = Bugzilla::Testopia::Category->new($self->{'category_id'}); + return $self->{'category'}; +} + +=head2 components + +Returns a reference to a list of bugzilla components assoicated with +this test case. + +=cut + +sub components { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'components'} if exists $self->{'components'}; + my $comps = $dbh->selectcol_arrayref( + "SELECT comp.id + FROM components AS comp + JOIN test_case_components AS tcc ON tcc.component_id = comp.id + JOIN test_cases ON tcc.case_id = test_cases.case_id + WHERE test_cases.case_id = ?", + {'Slice' => {}}, $self->{'case_id'}); + + my @comps; + foreach my $id (@$comps){ + my $comp = Bugzilla::Component->new($id); + my $prod = Bugzilla::Product->new($comp->product_id); + $comp->{'product_name'} = $prod->name; + push @comps, $comp; + } + $self->{'components'} = \@comps; + return $self->{'components'}; +} + +=head2 tags + +Returns a reference to a list of Bugzilla::Testopia::TestTag objects +associated with this case. + +=cut + +sub tags { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'tags'} if exists $self->{'tags'}; + my $tagids = $dbh->selectcol_arrayref("SELECT test_case_tags.tag_id + FROM test_case_tags + INNER JOIN test_tags ON test_case_tags.tag_id = test_tags.tag_id + WHERE case_id = ? + ORDER BY test_tags.tag_name", + undef, $self->{'case_id'}); + my @tags; + foreach my $id (@{$tagids}){ + push @tags, Bugzilla::Testopia::TestTag->new($id); + } + $self->{'tags'} = \@tags; + return $self->{'tags'}; +} + +=head2 plans + +Returns a reference to a list of Bugzilla::Testopia::TestPlan objects +associated with this case. + +=cut + +sub plans { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'plans'} if exists $self->{'plans'}; + my $ref = $dbh->selectcol_arrayref("SELECT plan_id + FROM test_case_plans + WHERE case_id = ? ORDER BY plan_id", + undef, $self->{'case_id'}); + my @plans; + foreach my $id (@{$ref}){ + push @plans, Bugzilla::Testopia::TestPlan->new($id); + } + $self->{'plans'} = \@plans; + return $self->{'plans'}; +} + +=head2 bugs + +Returns a reference to a list of Bugzilla::Bug objects +associated with this case. + +=cut + +sub bugs { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'bugs'} if exists $self->{'bugs'}; + my $ref = $dbh->selectall_arrayref( + "SELECT bug_id, case_run_id + FROM test_case_bugs + WHERE case_id = ?", + {'Slice' => {}}, $self->{'case_id'}); + my @bugs; + foreach my $row (@{$ref}){ + next unless Bugzilla->user->can_see_bug($row->{'bug_id'}); + my $bug = Bugzilla::Bug->new($row->{'bug_id'}, Bugzilla->user->id); + if ($row->{'case_run_id'}){ + my $cr = Bugzilla::Testopia::TestCaseRun->new($row->{'case_run_id'}); + next unless $cr; + $bug->{'build'} = $cr->build->name; + $bug->{'env'} = $cr->environment->name; + $bug->{'run_id'} = $cr->run_id; + $bug->{'case_run_id'} = $cr->id; + } + push @bugs, $bug; + } + $self->{'bugs'} = \@bugs; + return $self->{'bugs'}; +} + +=head2 bug_list + +Returns a comma separated list of bug ids associated with this case + +=cut + +sub bug_list { + my $self = shift; + return $self->{'bug_list'} if exists $self->{'bug_list'}; + my $dbh = Bugzilla->dbh; + my @bugs; + my $bugids = $dbh->selectcol_arrayref("SELECT bug_id + FROM test_case_bugs + WHERE case_id=?", + undef, $self->id); + my @visible; + foreach my $bugid (@{$bugids}){ + push @visible, $bugid if Bugzilla->user->can_see_bug($bugid); + } + $self->{'bug_list'} = join(",", @$bugids); + + return $self->{'bug_list'}; +} + +=head2 text + +Returns a hash reference representing the action and effect of this +case. + +=cut + + +sub text { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($version) = @_; + trick_taint($version) if $version; + return $self->{'text'} if exists $self->{'text'} && !$version; + + $version = $version || $self->version; + + my $text = $dbh->selectrow_hashref( + "SELECT action, effect, setup, breakdown, profiles.realname AS author, case_text_version AS version + FROM test_case_texts + INNER JOIN profiles on profiles.userid = test_case_texts.who + WHERE case_id = ? AND case_text_version = ?", + undef, ($self->{'case_id'}, $version)); + + return $text if scalar @_; + + $self->{'text'} = $text; + return $self->{'text'}; +} + +=head2 runs + +Returns a reference to a list of Bugzilla::Testopia::TestRun objects +associated with this case. + +=cut + +sub runs { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'runs'} if exists $self->{'runs'}; + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT t.run_id + FROM test_runs t + INNER JOIN test_case_runs r ON r.run_id = t.run_id + WHERE case_id = ?", + undef, $self->{'case_id'}); + my @runs; + foreach my $id (@{$ref}){ + push @runs, Bugzilla::Testopia::TestRun->new($id); + } + $self->{'runs'} = \@runs; + return $self->{'runs'}; +} + +sub run_count { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($runcount) = $dbh->selectrow_array( + "SELECT DISTINCT count(run_id) + FROM test_case_runs + WHERE case_id = ?", + undef, $self->id); + return $runcount; +} + +=head2 caseruns + +Returns a reference to a list of Bugzilla::Testopia::TestCaseRun objects +associated with this case. + +=cut + +sub caseruns { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'caseruns'} if exists $self->{'caseruns'}; + my $ref = $dbh->selectcol_arrayref("SELECT case_run_id + FROM test_case_runs + WHERE case_id = ?", + undef, $self->{'case_id'}); + my @runs; + foreach my $id (@{$ref}){ + push @runs, Bugzilla::Testopia::TestCaseRun->new($id); + } + $self->{'caseruns'} = \@runs; + return $self->{'caseruns'}; +} + +sub sortkey { + my $self = shift; + return $self->{'sortkey'} if exists $self->{'sortkey'}; + my $dbh = Bugzilla->dbh; + my ($sortkey) = $dbh->selectrow_array("SELECT MAX(sortkey) FROM test_cases"); + $self->{'sortkey'} = ++$sortkey; + return $self->{'sortkey'}; +} + +=head2 blocked + +Returns a reference to a list of Bugzilla::Testopia::TestCase objects +which are blocked by this test case. + +=cut + +sub blocked { + my ($self) = @_; + return $self->{'blocked'} if exists $self->{'blocked'}; + my @deps; + my $ref = _get_dep_lists("dependson", "blocked", $self->{'case_id'}); + foreach my $id (@{$ref}){ + push @deps, Bugzilla::Testopia::TestCase->new($id); + } + $self->{'blocked'} = \@deps; + return $self->{'blocked'}; +} + +sub blocked_list { + my ($self) = @_; + return $self->{'blocked_list'} if exists $self->{'blocked_list'}; + my @deps; + my $ref = _get_dep_lists("dependson", "blocked", $self->{'case_id'}); + $self->{'blocked_list'} = join(",", @$ref); + return $self->{'blocked_list'}; +} + +=head2 blocked_list_uncached + +Returns a space separated list of test cases that are blocked by this test case. +This method does not cache the blocked test cases so each call will result +in a database read. + +=cut + +sub blocked_list_uncached { + my ($self) = @_; + my $ref = _get_dep_lists("dependson", "blocked", $self->{'case_id'}); + return join(" ", @$ref) +} + +=head2 dependson + +Returns a reference to a list of Bugzilla::Testopia::TestCase objects +which depend on this test case. + +=cut + +sub dependson { + my ($self) = @_; + return $self->{'dependson'} if exists $self->{'dependson'}; + my @deps; + my $ref = _get_dep_lists("blocked", "dependson", $self->{'case_id'}); + foreach my $id (@{$ref}){ + push @deps, Bugzilla::Testopia::TestCase->new($id); + } + $self->{'dependson'} = \@deps; + return $self->{'dependson'}; +} + +sub dependson_list { + my ($self) = @_; + return $self->{'dependson_list'} if exists $self->{'dependson_list'}; + my @deps; + my $ref = _get_dep_lists("blocked", "dependson", $self->{'case_id'}); + $self->{'dependson_list'} = join(",", @$ref); + return $self->{'dependson_list'}; +} + +=head2 dependson_list_uncached + +Returns a space separated list of test cases that depend on this test case. +This method does not cache the dependent test cases so each call will result +in a database read. + +=cut + +sub dependson_list_uncached { + my ($self) = @_; + my $ref = _get_dep_lists("blocked", "dependson", $self->{'case_id'}); + return join(" ", @$ref) +} + +sub calculate_average_time { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $totalseconds; + my $i = 0; + foreach my $cr (@{$self->caseruns}){ + if ($cr->completion_time){ + $totalseconds += $cr->completion_time; + $i++; + } + } + + my $average = $i ? int($totalseconds / $i) : 0; + + my @time = gmtime($average); + my %time; + + $time{hr} = $time[2]; + $time{min} = $time[1]; + $time{sec} = $time[0]; + + return $time{hr}.":".$time{min}.":".$time{sec}; +} + + +=head1 TODO + +=head1 SEE ALSO + +TestPlan, TestRun, TestCaseRun + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::TestCase - Testopia Test Case object + +=head1 DESCRIPTION + +This module represents a test case in Testopia. Each test case must +be linked to one or more test plans. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::TestCase; + + $case = Bugzilla::Testopia::TestCase->new($case_id); + $case = Bugzilla::Testopia::TestCase->new(\%case_hash); + +=cut + +=head1 FIELDS + + case_id + case_status_id + category_id + priority_id + author_id + default_tester_id + creation_date + estimated_time + isautomated + sortkey + script + arguments + summary + requirement + alias + +=cut + +=head1 METHODS + +=cut +=head2 lookup_status + +Takes an ID of the status field and returns the value + +=cut +=head2 lookup_status_by_name + +Returns the id of the status name passed. + +=cut + +=head2 lookup_category + +Takes an ID of the category field and returns the value + +=cut + +=head2 lookup_category_by_name + +Returns the id of the category name passed. + +=cut + +=head2 lookup_priority + +Takes an ID of the priority field and returns the value + +=cut + +=head2 lookup_priority_by_name + +Returns the id of the priority name passed. + +=cut + +=head2 lookup_default_tester + +Takes an ID of the default_tester field and returns the value + +=cut + diff --git a/Bugzilla/Testopia/TestCaseRun.pm b/Bugzilla/Testopia/TestCaseRun.pm new file mode 100644 index 0000000..b72d89c --- /dev/null +++ b/Bugzilla/Testopia/TestCaseRun.pm @@ -0,0 +1,1193 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + + +package Bugzilla::Testopia::TestCaseRun; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::User; +use Bugzilla::Bug; +use Bugzilla::Config; +use Bugzilla::Constants; + +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Attachment; + +use Date::Format; +use Date::Parse; + +use base qw(Exporter Bugzilla::Object); +#@Bugzilla::Testopia::TestCaseRun::EXPORT = qw(lookup_status lookup_status_by_name); + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + case_run_id + run_id + case_id + assignee + testedby + case_run_status_id + case_text_version + build_id + environment_id + notes + running_date + close_date + iscurrent + sortkey + +=cut +use constant DB_TABLE => "test_case_runs"; +use constant ID_FIELD => "case_run_id"; +use constant NAME_FIELD => ""; +use constant DB_COLUMNS => qw( + case_run_id + run_id + case_id + assignee + testedby + case_run_status_id + case_text_version + build_id + environment_id + notes + running_date + close_date + iscurrent + sortkey +); + +use constant REQUIRED_CREATE_FIELDS => qw(case_id run_id build_id environment_id case_run_status_id); +use constant UPDATE_COLUMNS => qw(case_run_status_id case_text_version notes sortkey); + +use constant VALIDATORS => { + case_id => \&_check_case_id, + build_id => \&_check_build_id, + run_id => \&_check_run_id, + environment_id => \&_check_env_id, + case_text_version => \&_check_case_text_version, + case_run_status_id => \&_check_case_run_status_id, +}; + +sub report_columns { + my $self = shift; + my %columns; + # Changes here need to match Report.pm + $columns{'Build'} = "build"; + $columns{'Status'} = "case_run_status"; + $columns{'Environment'} = "environment"; + $columns{'Assignee'} = "assignee"; + $columns{'Tested By'} = "testedby"; + $columns{'Milestone'} = "milestone"; + $columns{'Case Tags'} = "case_tags"; + $columns{'Run Tags'} = "run_tags"; + $columns{'Requirement'} = "requirement"; + $columns{'Priority'} = "priority"; + $columns{'Default tester'} = "default_tester"; + $columns{'Category'} = "category"; + $columns{'Component'} = "component"; + my @result; + push @result, {'name' => $_, 'id' => $columns{$_}} foreach (sort(keys %columns)); + unshift @result, {'name' => '', 'id'=> ''}; + return \@result; + +} +############################### +#### Validators #### +############################### +sub _check_case_id { + my ($invocant, $id) = @_; + return Bugzilla::Testopia::Util::validate_test_id($id, 'case'); +} + +sub _check_run_id { + my ($invocant, $id) = @_; + return Bugzilla::Testopia::Util::validate_test_id($id, 'run'); +} + +sub _check_build_id { + my ($invocant, $id) = @_; + return Bugzilla::Testopia::Util::validate_test_id($id, 'build'); +} + +sub _check_env_id { + my ($invocant, $id) = @_; + return Bugzilla::Testopia::Util::validate_test_id($id, 'environment'); +} + +sub _check_case_run_status_id { + my ($invocant, $status) = @_; + $status = trim($status); + my $status_id; + if ($status =~ /^\d+$/){ + $status_id = Bugzilla::Testopia::Util::validate_selection($status, 'case_run_status_id', 'test_case_run_status'); + } + else { + $status_id = lookup_status_by_name($status); + } + ThrowUserError('invalid_status') unless $status_id; + return $status_id; +} + +sub _check_case_text_version { + my ($invocant, $version) = @_; + return $version =~ /\d+/ ? $version : 1; +} + +sub _check_assignee{ + my ($invocant, $tester) = @_; + $tester = trim($tester); + return unless $tester; + if ($tester =~ /^\d+$/){ + $tester = Bugzilla::User->new($tester); + return $tester->id; + } + else { + my $id = login_to_id($tester, THROW_ERROR); + return $id; + } +} + + +############################### +#### Mutators #### +############################### + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my ($param, $case_id, $build_id, $env_id) = (@_); + my $dbh = Bugzilla->dbh; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (!keys %$param || $param->{PREVALIDATED}){ + bless($param, $class); + return $param; + } + } + elsif ($case_id && detaint_natural($case_id) + && $build_id && detaint_natural($build_id) + && $env_id && detaint_natural($env_id)){ + + my $run_id = $param; + detaint_natural($case_id) || return undef; + ($param) = $dbh->selectrow_array( + "SELECT case_run_id FROM test_case_runs + WHERE case_id = ? + AND run_id = ? + AND build_id = ? + AND environment_id = ?", + undef, ($case_id, $run_id, $build_id, $env_id)); + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{iscurrent} = 1; + + my $self = $class->SUPER::insert_create_data($field_values); + + $self->set_as_current; + + return $self; +} + +############################### +#### Methods #### +############################### + +=head2 check_exists + +Checks for an existing entry with the same build and environment for this +case and run and switches self to that object. + +=cut + +sub switch { + my $self = shift; + my ($build_id, $env_id ,$run_id, $case_id) = @_; + + detaint_natural($build_id); + detaint_natural($env_id); + detaint_natural($run_id); + detaint_natural($case_id); + + $run_id ||= $self->{'run_id'}; + $case_id ||= $self->{'case_id'}; + $build_id ||= $self->{'build_id'}; + $env_id ||= $self->{'environment_id'}; + + my $dbh = Bugzilla->dbh; + my ($is) = $dbh->selectrow_array( + "SELECT case_run_id + FROM test_case_runs + WHERE run_id = ? + AND case_id = ? + AND build_id = ? + AND environment_id = ?", + undef, ($run_id, $case_id, $build_id, $env_id)); + + if ($is){ + $self = Bugzilla::Testopia::TestCaseRun->new($is); + } + else { + my $oldbuild = $self->{'build_id'}; + my $oldenv = $self->{'environment_id'}; + + $self = $self->create({ + 'run_id' => $self->{'run_id'}, + 'case_id' => $self->{'case_id'}, + 'assignee' => $self->{'assignee'}, + 'case_text_version' => $self->{'case_text_version'}, + 'build_id' => $build_id, + 'environment_id' => $env_id, + 'case_run_status_id' => IDLE, + }); + + if ($oldbuild != $build_id){ + my $build = Bugzilla::Testopia::Build->new($oldbuild); + my $note = "Build Changed by ". Bugzilla->user->login; + $note .= ". Old build: '". $build->name; + $note .= "' New build: '". $self->build->name; + $note .= "'. Resetting to IDLE."; + $self->append_note($note); + } + if ($oldenv != $env_id){ + my $environment = Bugzilla::Testopia::Environment->new($oldenv); + my $note = "Environment Changed by ". Bugzilla->user->login; + $note .= ". Old environment: '". $environment->name; + $note .= "' New environment: '". $self->environment->name; + $note .= "'. Resetting to IDLE."; + $self->append_note($note); + } + $self->set_as_current; + } + + return $self; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + my @bugs; + foreach my $b (@{$self->bugs}){ + push @bugs, { bug_id => $b->bug_id, closed => Bugzilla::Bug::is_open_state($b->{'bug_status'}) ? JSON::false : JSON::true}; + } + my $bugs = { bugs => \@bugs }; + + $obj->{'assignee_name'} = $self->assignee->login if $self->assignee; + $obj->{'requirement'} = $self->case->requirement if $self->case; + $obj->{'testedby'} = $self->testedby->login if $self->testedby; + $obj->{'status'} = $self->status; + $obj->{'build_name'} = $self->build->name if $self->build; + $obj->{'env_name'} = $self->environment->name if $self->environment; + $obj->{'env_id'} = $self->environment->id if $self->environment; + $obj->{'category'} = $self->case->category->name if $self->case && $self->case->category; + $obj->{'priority'} = $self->case->priority if $self->case; + $obj->{'bug_count'} = $self->bug_count; + $obj->{'case_summary'} = $self->case->summary if $self->case; + $obj->{'component'} = @{$self->case->components}[0]->name if ($self->case && scalar @{$self->case->components}); + $obj->{'type'} = $self->type; + $obj->{'id'} = $self->id; + $obj->{'sortkey'} = $self->sortkey; + $obj->{'bug_list'} = $bugs; + + return $json->encode($obj); +} + +=head2 _update_fields + +Update this case-run in the database if a change is made to an +updatable field. + +=cut + +sub _update_fields{ + my $self = shift; + my ($newvalues) = @_; + my $dbh = Bugzilla->dbh; + + if ($newvalues->{'case_run_status_id'} && $newvalues->{'case_run_status_id'} == FAILED){ + $self->_update_deps(BLOCKED); + } + elsif ($newvalues->{'case_run_status_id'} && $newvalues->{'case_run_status_id'} == PASSED){ + $self->_update_deps(IDLE); + } + + $dbh->bz_start_transaction(); + foreach my $field (keys %{$newvalues}){ + $dbh->do("UPDATE test_case_runs + SET $field = ? WHERE case_run_id = ?", + undef, $newvalues->{$field}, $self->{'case_run_id'}); + $self->{$field} = $newvalues->{$field}; + } + $dbh->bz_commit_transaction(); + + return $self->{'case_run_id'}; +} + +=head2 set_as_current + +Sets this case-run as the current or active one in the history +list of case-runs of this build and case_id + +=cut + +sub set_as_current { + my $self = shift; + my ($caserun) = @_; + $caserun = $self->{'case_run_id'} unless defined $caserun; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); + $dbh->do("UPDATE test_case_runs + SET iscurrent = 0 + WHERE case_id = ? AND run_id = ?", + undef, ($self->case_id, $self->run_id)); + + $dbh->do("UPDATE test_case_runs + SET iscurrent = 1 + WHERE case_run_id = ?", + undef, $caserun); + $dbh->bz_commit_transaction(); +} + +=head2 set_status + +Sets the status on a case-run and updates the close_date and testedby +if the status is a closed status. + +=cut + +sub set_status { + my $self = shift; + my ($status_id, $update_bugs) = @_; + $status_id = $self->_check_case_run_status_id($status_id); + return if $self->status_id == $status_id; + my $oldstatus = $self->status; + my $newstatus = lookup_status($status_id); + + $self->_update_fields({'case_run_status_id' => $status_id}); + if ($status_id == IDLE){ + $self->_update_fields({'close_date' => undef}); + $self->_update_fields({'testedby' => undef}); + $self->{'close_date'} = undef; + $self->{'testedby'} = undef; + } + elsif ($status_id == RUNNING || $status_id == PAUSED){ + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + $self->_update_fields({'running_date' => $timestamp}) if $status_id == RUNNING; + $self->_update_fields({'close_date' => undef}); + $self->{'close_date'} = undef; + } + else { + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + $self->_update_fields({'close_date' => $timestamp}); + $self->_update_fields({'testedby' => Bugzilla->user->id}); + $self->{'close_date'} = $timestamp; + $self->{'testedby'} = Bugzilla->user->id; + $self->update_bugs('REOPENED') if ($status_id == FAILED && $update_bugs); + $self->update_bugs('VERIFIED') if ($status_id == PASSED && $update_bugs); + } + $self->set_as_current; + my $note = "Status changed from $oldstatus to $newstatus by ". Bugzilla->user->login; + $note .= " for build '". $self->build->name ."' and environment '". $self->environment->name; + $self->append_note($note); + $self->{'case_run_status_id'} = $status_id; + delete $self->{'status'}; +} + +sub set_sortkey { + my $self = shift; + my ($sortkey) = @_; + my $dbh = Bugzilla->dbh; + detaint_natural($sortkey); + $dbh->do("UPDATE test_case_runs SET sortkey = ? + WHERE case_id = ? AND run_id = ?", + undef, ($sortkey, $self->case_id, $self->run_id)); + + $self->{'sortkey'} = $sortkey; +} + +=head2 set_assignee + +Sets the assigned tester for the case-run + +=cut + +sub set_assignee { + my $self = shift; + my ($user_id) = @_; + + my $assignee = $self->_check_assignee($user_id); + my $oldassignee = $self->assignee->login; + my $newassignee = Bugzilla::User->new($assignee); + + $self->_update_fields({'assignee' => $assignee}); + $self->{'assignee'} = $assignee; + + my $note = "Assignee changed from $oldassignee to ". $newassignee->login; + $note .= " by ". Bugzilla->user->login; + $note .= " for build '". $self->build->name; + $note .= "' and environment '". $self->environment->name; + $self->append_note($note); +} + +=head2 append_note + +Updates the notes field for the case-run + +=cut + +sub append_note { + my $self = shift; + my ($note) = @_; + return unless $note; + my $timestamp = time2str("%c", time()); + $note = "$timestamp: $note"; + if ($self->{'notes'}){ + $note = $self->{'notes'} . "\n" . $note; + } + $self->_update_fields({'notes' => $note}); + $self->{'notes'} = $note; +} + +=head2 _update_deps + +Private method for updating blocked test cases. If the pre-requisite +case fails, the blocked test cases in a run get a status of BLOCKED +if it passes they are set back to IDLE. This only happens to the +current case run and only if it doesn't already have a closed status. +=cut + +sub _update_deps { + my $self = shift; + my ($status) = @_; + my $deplist = $self->case->get_dep_tree; + return unless $deplist; + + my $dbh = Bugzilla->dbh; + $dbh->bz_start_transaction(); + my $caseruns = $dbh->selectcol_arrayref( + "SELECT case_run_id + FROM test_case_runs + WHERE iscurrent = 1 + AND run_id = ? + AND case_run_status_id IN(". join(',', (IDLE,RUNNING,PAUSED,BLOCKED)) .") + AND case_id IN (". join(',', @$deplist) .")", + undef, $self->{'run_id'}); + my $sth = $dbh->prepare_cached( + "UPDATE test_case_runs + SET case_run_status_id = ? + WHERE case_run_id = ?"); + + foreach my $id (@$caseruns){ + $sth->execute($status, $id); + } + $dbh->bz_commit_transaction(); + + $self->{'updated_deps'} = $caseruns; +} + +=head2 get_case_run_list + +Returns a reference to a list of case-runs for the given case and run + +=cut + +sub get_case_run_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectcol_arrayref( + "SELECT case_run_id FROM test_case_runs + WHERE case_id = ? AND run_id = ?", undef, + ($self->{'case_id'}, $self->{'run_id'})); + + return $ref; +} + +sub get_history { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT tcr.case_run_id, tcr.close_date, tcr.iscurrent, tb.name AS build_name, + te.name as env_name, p.login_name AS testedby, tcrs.name AS status_name + FROM test_case_runs tcr + INNER JOIN test_builds tb ON tcr.build_id = tb.build_id + INNER JOIN test_environments te ON tcr.environment_id = te.environment_id + INNER JOIN test_case_run_status tcrs ON tcr.case_run_status_id = tcrs.case_run_status_id + LEFT JOIN profiles p ON tcr.testedby = p.userid + WHERE case_id = ? AND run_id = ?", {'Slice' =>{}}, + ($self->{'case_id'}, $self->{'run_id'})); + + return $ref; + +} + +=head2 get_status_list + +Returns a list reference of the legal statuses for a test case-run + +=cut + +sub get_status_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT case_run_status_id AS id, name + FROM test_case_run_status + ORDER BY sortkey", {'Slice' =>{}}); + + return $ref +} + +=head2 attach_bug + +Attaches the specified bug to this test case-run + +=cut + +sub attach_bug { + my $self = shift; + my ($bugs, $caserun_id) = @_; + $bugs = Bugzilla::Testopia::TestCase->_check_bugs($bugs, "ATTACH"); + + $caserun_id ||= $self->{'case_run_id'}; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); + foreach my $bug (@$bugs){ + trick_taint($bug) if $bug; + trick_taint($caserun_id) if $caserun_id; + my ($exists) = $dbh->selectrow_array( + "SELECT bug_id + FROM test_case_bugs + WHERE case_run_id=? + AND bug_id=?", + undef, ($caserun_id, $bug)); + if ($exists) { + $dbh->bz_commit_transaction(); + return; + } + my ($check) = $dbh->selectrow_array( + "SELECT bug_id + FROM test_case_bugs + WHERE case_id=? + AND bug_id=? + AND case_run_id=?", + undef, ($caserun_id, $bug, undef)); + + if ($check){ + $dbh->do("UPDATE test_case_bugs + SET test_case_run_id = ? + WHERE case_id = ? + AND bug_id = ?", + undef, ($bug, $self->{'case_run_id'})); + } + else{ + $dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id, case_id) + VALUES(?,?,?)", undef, ($bug, $self->{'case_run_id'}, $self->{'case_id'})); + } + } + $dbh->bz_commit_transaction(); +} + +=head2 detach_bug + +Removes the association of the specified bug from this test case-run + +=cut + +sub detach_bug { + my $self = shift; + my ($bug) = @_; + my $dbh = Bugzilla->dbh; + trick_taint($bug) if $bug; + $dbh->do("DELETE FROM test_case_bugs + WHERE bug_id = ? + AND case_run_id = ?", + undef, ($bug, $self->{'case_run_id'})); + +} + +=head2 update_bugs + +Updates bug status depending on whether the case passed or failed. If +the case failed it will reopen any attached bugs that are closed. If it +passed it will mark RESOLVED bugs VERIFIED. + +=cut + +sub update_bugs { + my $self = shift; + my ($status) = @_; + my $resolution; + my $dbh = Bugzilla->dbh; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + foreach my $bug (@{$self->bugs}){ + my $oldstatus = $bug->bug_status; + my $oldresolution = $bug->resolution; + + next if ($status eq 'VERIFIED' && $oldstatus ne 'RESOLVED'); + next if ($status eq 'REOPENED' && $oldstatus !~ /(RESOLVED|VERIFIED|CLOSED)/); + next if $oldresolution eq 'DUPLICATE'; + if ($status eq 'REOPENED'){ + $resolution = ''; + } + else{ + $resolution = $oldresolution; + } + my $comment = "Status updated by Testopia: ". Bugzilla->params->{"urlbase"}; + $comment .= "tr_show_case.cgi?case_id=" . $self->case->id; + + $dbh->bz_start_transaction(); + $dbh->do("UPDATE bugs + SET bug_status = ?, + resolution = ?, + delta_ts = ? + WHERE bug_id = ?", + undef,($status, $resolution, $timestamp, $bug->bug_id)); + LogActivityEntry($bug->bug_id, 'bug_status', $oldstatus, + $status, Bugzilla->user->id, $timestamp); + LogActivityEntry($bug->bug_id, 'resolution', $bug->resolution, '', + Bugzilla->user->id, $timestamp) if ($status eq 'REOPENED'); + AppendComment($bug->bug_id, Bugzilla->user->id, $comment, + !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'}), $timestamp); + + $dbh->bz_commit_transaction(); + } +} + +=head2 obliterate + +Removes this caserun, its history, and all things that reference it. + +=cut + +sub obliterate { + my $self = shift; + my $single = shift; + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare_cached("DELETE FROM test_case_bugs WHERE case_run_id = ?"); + foreach my $id (@{$self->get_case_run_list}){ + $sth->execute($id); + } + + if ($single){ + $dbh->do("DELETE FROM test_case_runs WHERE case_run_id = ?", + undef, ($self->id)); + } + else { + $dbh->do("DELETE FROM test_case_runs WHERE case_id = ? AND run_id = ?", + undef, ($self->case_id, $self->run_id)); + } + return 1; +} + +############################### +#### Accessors #### +############################### + +=head1 ACCESSOR METHODS + +=head2 id + +Returns the ID of the object + +=head2 testedby + +Returns a Bugzilla::User object representing the user that closed +this case-run. + +=head2 assignee + +Returns a Bugzilla::User object representing the user assigned to +update this case-run. + +=head2 case_text_version + +Returns the version of the test case document that this case-run +was run against. + +=head2 notes + +Returns the notes of the object + +=head2 close_date + +Returns the time stamp of when this case-run was closed + +=head2 iscurrent + +Returns true if this is the current case-run in the history list + +=head2 status_id + +Returns the status id of the object + +=head2 sortkey + +Returns the sortkey of the object + +=head2 isprivate + +Returns the true if this case-run is private. + +=cut + +=head2 updated_deps + +Returns a reference to a list of dependent caseruns that were updated + +=cut + +sub id { return $_[0]->{'case_run_id'}; } +sub case_id { return $_[0]->{'case_id'}; } +sub run_id { return $_[0]->{'run_id'}; } +sub testedby { return Bugzilla::User->new($_[0]->{'testedby'}); } +sub assignee { return Bugzilla::User->new($_[0]->{'assignee'}); } +sub case_text_version { return $_[0]->{'case_text_version'}; } +sub running_date { return $_[0]->{'running_date'}; } +sub close_date { return $_[0]->{'close_date'}; } +sub iscurrent { return $_[0]->{'iscurrent'}; } +sub status_id { return $_[0]->{'case_run_status_id'}; } +sub sortkey { return $_[0]->{'sortkey'}; } +sub isprivate { return $_[0]->{'isprivate'}; } +sub updated_deps { return $_[0]->{'updated_deps'}; } + +=head2 type + +Returns 'case' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'caserun'; + return $self->{'type'}; +} + +=head2 notes + +Returns the cumulative notes of all caserun records of this case and run. + +=cut + +sub notes { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $notes = $dbh->selectcol_arrayref( + "SELECT notes + FROM test_case_runs + WHERE case_id = ? AND run_id = ? + ORDER BY case_run_id", + undef,($self->case_id, $self->run_id)); + + return join("\n", @$notes); +} + +=head2 run + +Returns the TestRun object that this case-run is associated with + +=cut + +# The potential exists for creating a circular reference here. +sub run { + my $self = shift; + return $self->{'run'} if exists $self->{'run'}; + require Bugzilla::Testopia::TestRun; + $self->{'run'} = Bugzilla::Testopia::TestRun->new($self->{'run_id'}); + return $self->{'run'}; +} + +=head2 case + +Returns the TestCase object that this case-run is associated with + +=cut + +# The potential exists for creating a circular reference here. +sub case { + my $self = shift; + return $self->{'case'} if exists $self->{'case'}; + require Bugzilla::Testopia::TestCase; + $self->{'case'} = Bugzilla::Testopia::TestCase->new($self->{'case_id'}); + return $self->{'case'}; +} + +=head2 build + +Returns the Build object that this case-run is associated with + +=cut + +sub build { + my $self = shift; + return $self->{'build'} if exists $self->{'build'}; + $self->{'build'} = Bugzilla::Testopia::Build->new($self->{'build_id'}); + return $self->{'build'}; +} + +=head2 environment + +Returns the Build object that this case-run is associated with + +=cut + +sub environment { + my $self = shift; + return $self->{'environment'} if exists $self->{'environment'}; + $self->{'environment'} = Bugzilla::Testopia::Environment->new($self->{'environment_id'}); + return $self->{'environment'}; +} + +=head2 status + +Looks up the status name of the associated status_id for this object + +=cut + +sub status { + my $self = shift; + my $dbh = Bugzilla->dbh; + ($self->{'status'}) = $dbh->selectrow_array( + "SELECT name FROM test_case_run_status + WHERE case_run_status_id=?", undef, + $self->{'case_run_status_id'}); + return $self->{'status'}; +} + +=head2 attachments + +Returns a reference to a list of attachments associated with this +case. + +=cut + +sub attachments { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'attachments'} if exists $self->{'attachments'}; + + my $attachments = $dbh->selectall_arrayref( + "SELECT attachment_id, case_run_id + FROM test_case_attachments + WHERE case_id = ?", + { Slice=>{} }, $self->case_id); + + my @attachments; + foreach my $attach (@{$attachments}){ + my $att = Bugzilla::Testopia::Attachment->new($attach->{attachment_id}); + $att->{'caserun_id'} = $self->id if $attach->{case_run_id}; + push @attachments, $att; + } + $self->{'attachments'} = \@attachments; + return $self->{'attachments'}; + +} + +=head2 bugs + +Returns a list of Bugzilla::Bug objects associated with this case-run + +=cut + +sub bugs { + my $self = shift; + #return $self->{'bug'} if exists $self->{'bug'}; + my $dbh = Bugzilla->dbh; + my @bugs; + my $bugids = $dbh->selectcol_arrayref("SELECT bug_id + FROM test_case_bugs + WHERE case_run_id=?", + undef, $self->{'case_run_id'}); + foreach my $bugid (@{$bugids}){ + push @bugs, Bugzilla::Bug->new($bugid, Bugzilla->user->id) if Bugzilla->user->can_see_bug($bugid); + } + $self->{'bugs'} = \@bugs; #join(",", @$bugids); + + return $self->{'bugs'}; +} + +=head2 bug_list + +Returns a comma separated list of bug ids associated with this case-run + +=cut + +sub bug_list { + my $self = shift; + return $self->{'bug_list'} if exists $self->{'bug_list'}; + my $dbh = Bugzilla->dbh; + my @bugs; + my $bugids = $dbh->selectcol_arrayref("SELECT DISTINCT bug_id + FROM test_case_bugs + WHERE case_run_id=?", + undef, $self->id); + my @visible; + foreach my $bugid (@{$bugids}){ + push @visible, $bugid if Bugzilla->user->can_see_bug($bugid); + } + $self->{'bug_list'} = join(",", @$bugids); + + return $self->{'bug_list'}; +} + +=head2 bug_count + +Retuns a count of the bugs associated with this case-run + +=cut + +sub bug_count{ + my $self = shift; + return $self->{'bug_count'} if exists $self->{'bug_count'}; + my $dbh = Bugzilla->dbh; + + $self->{'bug_count'} = $dbh->selectrow_array("SELECT COUNT(bug_id) + FROM test_case_bugs + WHERE case_run_id=?", + undef, $self->{'case_run_id'}); + return $self->{'bug_count'}; +} + +=head2 get_buglist + +Returns a comma separated string off bug ids associated with +this case-run + +=cut + +sub get_buglist { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $bugids = $dbh->selectcol_arrayref("SELECT bug_id + FROM test_case_bugs + WHERE case_run_id=?", + undef, $self->{'case_run_id'}); + return join(',', @{$bugids}); +} + +=head2 is_open_status + +Returns true if the status of this case-run is an open status + +=cut + +sub is_open_status { + my $self = shift; + my $status = shift; + my @open_status_list = (IDLE, RUNNING, PAUSED, ERROR); + return 1 if lsearch(\@open_status_list, $status) > -1; +} + +=head2 is_closed_status + +Returns true if the status of this case-run is a closed status + +=cut + +sub is_closed_status { + my $self = shift; + my $status = shift; + my @closed_status_list = (PASSED, FAILED, BLOCKED); + return 1 if lsearch(\@closed_status_list, $status) > -1; +} + +=head2 canview + +Returns true if the logged in user has rights to view this case-run. + +=cut + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->run->plan->get_user_rights(Bugzilla->user->id) & TR_READ; + return 0; +} + +=head2 canedit + +Returns true if the logged in user has rights to edit this case-run. + +=cut + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 0 if $self->run->stop_date; + return 1 if $self->run->plan->get_user_rights(Bugzilla->user->id) & TR_ADMIN; + if ($self->status_id == RUNNING){ + return 1 if $self->run->manager->id == Bugzilla->user->id; + return 0 unless $self->assignee->id && $self->assignee->id == Bugzilla->user->id; + } + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->run->plan->get_user_rights(Bugzilla->user->id) & TR_WRITE; + + return 0; +} + +=head2 candelete + +Returns true if the logged in user has rights to delete this case-run. + +=cut + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 0 unless Bugzilla->params->{"allow-test-deletion"}; + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + return 1 if $self->run->plan->get_user_rights(Bugzilla->user->id) & TR_DELETE; + return 0; +} + +sub completion_time { + my $self = shift; + my $dbh = Bugzilla->dbh; + if (str2time($self->running_date) && str2time($self->close_date)){ + my $seconds = str2time($self->close_date) - str2time($self->running_date); + return $seconds; + } + return 0; +} + +############################### +#### Functions #### +############################### + +=head2 lookup_status + +Returns the status name of the given case_run_status_id + +=cut + +sub lookup_status { + my ($status_id) = @_; + detaint_natural($status_id); + my $dbh = Bugzilla->dbh; + my ($status) = $dbh->selectrow_array( + "SELECT name + FROM test_case_run_status + WHERE case_run_status_id = ?", + undef, $status_id); + return $status; +} + +=head2 lookup_status_by_name + +Returns the id of the status name passed. + +=cut + +sub lookup_status_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + + my ($value) = $dbh->selectrow_array( + "SELECT case_run_status_id + FROM test_case_run_status + WHERE name = ?", + undef, $name); + return $value; +} + +1; +__END__ +=head1 NAME + +Bugzilla::Testopia::TestCaseRun - Testopia Test Case Run object + +=head1 DESCRIPTION + +This module represents a test case run in Testopia. +A test case run is a record in the test_case_runs table which joins +test cases to test runs. Basically, for each test run a selction of +test cases is made to be included in that run. As a test run +progresses, testers set statuses on each of the cases in the run. +If the build is changed on a case-run with a status, a clone of that +case-run is made in the table for historical purposes. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::TestCaseRun; + + $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id); + $caserun = Bugzilla::Testopia::TestCaseRun->new(\%caserun_hash); + +=cut + +=head1 METHODS + +=head2 new + +Instantiate a new case run. This takes a single argument +either a test case ID or a reference to a hash containing keys +identical to a test case-run's fields and desired values. + +=cut + +=head2 _init + +Private constructor for this object + +=cut + + +=head1 SEE ALSO + +TestCase TestRun + +=head1 AUTHOR + +Greg Hendricks + +=cut + + diff --git a/Bugzilla/Testopia/TestPlan.pm b/Bugzilla/Testopia/TestPlan.pm new file mode 100644 index 0000000..1c362eb --- /dev/null +++ b/Bugzilla/Testopia/TestPlan.pm @@ -0,0 +1,1469 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +package Bugzilla::Testopia::TestPlan; + +use strict; + +use Bugzilla::User; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Version; +use Bugzilla::Bug; + +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Attachment; + +use Text::Diff; +use JSON; + +use base qw(Exporter Bugzilla::Object); +@Bugzilla::Testopia::TestPlan::EXPORT = qw(lookup_type_by_name lookup_type); + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + plan_id + product_id + author_id + type_id + default_product_version + name + creation_date + isactive + +=cut + +use constant DB_TABLE => "test_plans"; +use constant NAME_FIELD => "name"; +use constant ID_FIELD => "plan_id"; +use constant DB_COLUMNS => qw( + plan_id + product_id + author_id + type_id + default_product_version + name + creation_date + isactive +); + +use constant REQUIRED_CREATE_FIELDS => qw(product_id author_id type_id default_product_version name); +use constant UPDATE_COLUMNS => qw(product_id type_id default_product_version name isactive); + +use constant VALIDATORS => { + product_id => \&_check_product, + author_id => \&_check_author, + type_id => \&_check_type, + isactive => \&_check_isactive, +}; + +use constant NAME_MAX_LENGTH => 255; + +sub report_columns { + my $self = shift; + my %columns; + # Changes here need to match Report.pm + $columns{'Type'} = "plan_type"; + $columns{'Version'} = "default_product_version"; + $columns{'Product'} = "product"; + $columns{'Archived'} = "archived"; + $columns{'Tags'} = "tags"; + $columns{'Author'} = "author"; + my @result; + push @result, {'name' => $_, 'id' => $columns{$_}} foreach (sort(keys %columns)); + unshift @result, {'name' => '', 'id'=> ''}; + return \@result; + +} + +############################### +#### Validators #### +############################### + +sub _check_product { + my ($invocant, $product_id) = @_; + + if (ref $invocant){ + ThrowUserError("plan-has-children") + if (($invocant->test_case_count || $invocant->test_run_count) && $invocant->product_id != $product_id); + } + + $product_id = trim($product_id); + my $product; + if ($product_id !~ /^\d+$/ ){ + $product = Bugzilla::Product::check_product($product_id); + $product = Bugzilla::Testopia::Product->new($product->id); + } + else { + $product = Bugzilla::Testopia::Product->new($product_id); + } + + ThrowUserError("invalid-test-id-non-existent", {'id' => $product_id, 'type' => 'product'}) unless $product; + ThrowUserError("testopia-create-denied", {'object' => 'plan'}) unless $product->canedit; + if (ref $invocant){ + $invocant->{'product'} = $product; + return $product->id; + } + return $product; +} + +sub _check_author { + my ($invocant, $author) = @_; + $author = trim($author); + if ($author =~ /^\d+$/){ + $author = Bugzilla::User->new($author); + } + else { + my $id = login_to_id($author, THROW_ERROR); + return $id; + } + return $author->id; +} + +sub _check_type { + my ($invocant, $type_id) = @_; + $type_id = trim($type_id); + trick_taint($type_id); + if ($type_id !~ /^\d+$/){ + $type_id = lookup_type_by_name($type_id) || $type_id; + } + Bugzilla::Testopia::Util::validate_selection($type_id, 'type_id', 'test_plan_types'); + return $type_id; +} + +sub _check_product_version { + my ($invocant, $version, $product) = @_; + if (ref $invocant){ + $product = $invocant->product; + } + $version = trim($version); + $version = Bugzilla::Version->check({product => $product, name => $version}); + return $version->name; +} + +sub _check_isactive { + my ($invocant, $isactive) = @_; + ThrowCodeError('bad_arg', {argument => 'isactive', function => 'set_isactive'}) unless ($isactive =~ /(1|0)/); + return $isactive; +} + +############################### +#### Mutators #### +############################### +sub set_name { $_[0]->set('name', $_[1]); } +sub set_type { $_[0]->set('type_id', $_[1]); } +sub set_isactive { $_[0]->set('isactive', $_[1]); } +sub set_product_id { $_[0]->set('product_id', $_[1]); } +sub set_default_product_version { + my ($self, $value) = @_; + $value = $self->_check_product_version($value); + $self->set('default_product_version', $value); +} + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + if (keys %$param){ + bless($param, $class); + return $param; + } + bless($param, $class); + return $param; + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $product = $params->{product_id}; + + $params->{default_product_version} = $class->_check_product_version($params->{default_product_version}, $product); + $params->{product_id} = $product->id; + + return $params; +} + +sub create { + my ($class, $params) = @_; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + + $field_values->{creation_date} = Bugzilla::Testopia::Util::get_time_stamp(); + $field_values->{isactive} = 1; + + #We have to handle the plan document text a bit differently since it has its own table. + my $plan_document = $field_values->{text}; + + delete $field_values->{text}; + + my $self = $class->SUPER::insert_create_data($field_values); + + $self->store_text($self->id, $field_values->{'author_id'}, $plan_document, $field_values->{creation_date}); + + # Add permissions for the plan + $self->add_tester($self->{'author_id'},15); + if (Bugzilla->params->{'testopia-default-plan-testers-regexp'}) { + $self->set_tester_regexp( Bugzilla->params->{"testopia-default-plan-testers-regexp"}, 3); + $self->derive_regexp_testers(Bugzilla->params->{'testopia-default-plan-testers-regexp'}); + } + + # Create default category + unless (scalar @{$self->product->categories}){ + require Bugzilla::Testopia::Category; + my $category = Bugzilla::Testopia::Category->create( + {'name' => '--default--', + 'description' => 'Default product category for test cases', + 'product_id' => $self->product->id }); + } + delete $self->{'product'}; + return $self; +} + +sub update { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + $dbh->bz_start_transaction(); + + my $changed = $self->SUPER::update(); + + foreach my $field (keys %$changed){ + Bugzilla::Testopia::Util::log_activity('plan', $self->id, $field, $timestamp, $changed->{$field}->[0], $changed->{$field}->[1]); + } + $dbh->bz_commit_transaction(); +} + +############################### +#### Methods #### +############################### + +=head2 store_text + +Stores the test plan document in the test_plan_texts +table. Used by both store and copy. Accepts the the test plan id, +author id, text, and a an optional timestamp. + +=cut + +sub store_text { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($key, $author, $text, $timestamp) = @_; + if (!defined $timestamp){ + ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); + } + $text ||= ''; + trick_taint($text); + + my $version = $self->version || 0; + $dbh->do("INSERT INTO test_plan_texts + (plan_id, plan_text_version, who, creation_ts, plan_text) + VALUES(?,?,?,?,?)", + undef, $key, ++$version, $author, + $timestamp, $text); + + $self->{'version'} = $version; +} + +=head2 clone + +Creates a copy of this test plan. Accepts the name of the new plan +and a boolean representing whether to copy the plan document as well. + +=cut + +sub clone { + my $self = shift; + my ($name, $author, $product_id, $version, $store_doc) = @_; + $store_doc = 1 unless defined($store_doc); + my $dbh = Bugzilla->dbh; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'plan_id'} DB_COLUMNS); + my ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); + + $dbh->do("INSERT INTO test_plans ($columns) VALUES (?,?,?,?,?,?,?)", + undef, ($product_id, $author, + $self->{'type_id'}, $version, $name, + $timestamp, 1)); + my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' ); + my $text = $store_doc ? $self->text->{'plan_text'} : ''; + $self->store_text($key, $self->{'author_id'}, $text, $timestamp); + return $key; + +} + +=head2 toggle_archive + +Toggles the archive bit on the plan. + +=cut + +sub toggle_archive { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $oldvalue = $self->isactive; + my $newvalue = $oldvalue == 1 ? 0 : 1; + $self->set_isactive($newvalue); + $self->update; +} + +=head2 add_tag + +Associates a tag with this test plan. Takes the tag_id of the tag +to link. + +=cut + +sub add_tag { + my $self = shift; + my $dbh = Bugzilla->dbh; + my @tags; + foreach my $t (@_){ + if (ref $t eq 'ARRAY'){ + push @tags, $_ foreach @$t; + } + else{ + push @tags, split(',', $t); + } + } + + foreach my $name (@tags){ + my $tag = Bugzilla::Testopia::TestTag->create({'tag_name' => $name}); + $tag->attach($self); + } +} + +=head2 remove_tag + +Removes a tag from this plan. Takes the tag_id of the tag to remove. + +=cut + +sub remove_tag { + my $self = shift; + my ($tag_name) = @_; + my $tag = Bugzilla::Testopia::TestTag->check_tag($tag_name); + ThrowUserError('testopia-unknown-tag', {'name' => $tag}) unless $tag; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_plan_tags + WHERE tag_id=? AND plan_id=?", + undef, ($tag->id, $self->{'plan_id'})); +} + +sub get_used_categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT test_case_categories.category_id AS id, name + FROM test_case_categories + JOIN test_cases ON test_cases.category_id = test_case_categories.category_id + JOIN test_case_plans ON test_cases.case_id = test_case_plans.case_id + WHERE plan_id = ? + ORDER BY name", + {'Slice'=>{}}, $self->id); + + return $ref; +} + +=head2 get_plan_types + +Returns a list of types from the test_plan_types table + +=cut + +sub get_plan_types { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $types = $dbh->selectall_arrayref( + "SELECT type_id AS id, name + FROM test_plan_types + ORDER BY name", + {"Slice"=>{}}); + return $types; + +} + +=head2 last_changed + +Returns the date of the last change in the history table + +=cut + +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_plan_activity + WHERE plan_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; +} + +=head2 plan_type_ref + +Returns a type name matching the given type id + +=cut + +sub plan_type_ref { + my $self = shift; + my $type_id = shift; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->selectrow_hashref( + "SELECT type_id AS id, name, description + FROM test_plan_types + WHERE type_id = ?", + undef, $type_id); + + return $type; +} + +=head2 check_plan_type + +Returns true if a type with the given name exists + +=cut + +sub check_plan_type { + my $self = shift; + my $name = shift; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->selectrow_hashref( + "SELECT 1 + FROM test_plan_types + WHERE name = ?", + undef, $name); + + return $type; +} + +sub check_tester { + my $self = shift; + my $userid = shift; + my $dbh = Bugzilla->dbh; + + my ($exists) = $dbh->selectrow_array( + "SELECT 1 + FROM test_plan_permissions + WHERE userid = ? AND plan_id = ? AND grant_type = ?", + undef, ($userid, $self->id, GRANT_DIRECT)); + + return $exists; + +} + +=head2 update_plan_type + +Update the given type + +=cut + +sub update_plan_type { + my $self = shift; + my ($type_id, $name, $desc) = @_; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->do( + "UPDATE test_plan_types + SET name = ?, description = ? + WHERE type_id = ?", + undef, ($name, $desc, $type_id)); + +} + +=head2 add_plan_type + +Add the given type + +=cut + +sub add_plan_type { + my $self = shift; + my ($name, $desc) = @_; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->do( + "INSERT INTO test_plan_types (name, description) VALUES(?, ?)", + undef, ($name, $desc)); +} + +=head2 get_fields + +Returns a list of fields from the fielddefs table associated with +a plan + +=cut + +sub get_fields { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $types = $dbh->selectall_arrayref( + "SELECT fieldid AS id, description AS name + FROM test_fielddefs + WHERE table_name=?", + {"Slice"=>{}}, "test_plans"); + unshift @$types, {id => 'text', name => 'Document'}; + unshift @$types, {id => '[Creation]', name => '[Created]'}; + return $types; +} + +=head2 get_text_versions + +Returns the list of versions of the plan document. + +=cut + +sub get_text_versions { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $versions = $dbh->selectall_arrayref( + "SELECT plan_text_version AS id, plan_text_version AS name + FROM test_plan_texts + WHERE plan_id = ? + ORDER BY plan_text_version", + {'Slice' =>{}}, $self->id); + return $versions; +} + +=head2 diff_plan_doc + +Returns either the diff of the latest version with a new text +or two numerical versions. + +=cut + +sub diff_plan_doc { + my $self = shift; + my ($new, $old) = @_; + $old ||= $self->version; + my $dbh = Bugzilla->dbh; + my $newdoc; + my $text = $new; + if (detaint_natural($new)){ + # we are looking for a version + $newdoc = $dbh->selectrow_array( + "SELECT plan_text FROM test_plan_texts + WHERE plan_id = ? AND plan_text_version = ?", + undef, ($self->{'plan_id'}, $new)); + } + else { + $newdoc = $text; + } + detaint_natural($old); + my $olddoc = $dbh->selectrow_array( + "SELECT plan_text FROM test_plan_texts + WHERE plan_id = ? AND plan_text_version = ?", + undef, ($self->{'plan_id'}, $old)); + my $diff = diff(\$newdoc, \$olddoc); + return $diff +} + +=head2 history + +Returns a reference to a list of history entries from the +test_plan_activity table. + +=cut + +sub history { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT defs.description AS what, + p.login_name AS who, a.changed, a.oldvalue, a.newvalue + FROM test_plan_activity AS a + JOIN test_fielddefs AS defs ON a.fieldid = defs.fieldid + JOIN profiles AS p ON a.who = p.userid + WHERE a.plan_id = ?", + {'Slice'=>{}}, $self->{'plan_id'}); + foreach my $row (@$ref){ + if ($row->{'what'} eq 'Product'){ + $row->{'oldvalue'} = $self->lookup_product($row->{'oldvalue'}); + $row->{'newvalue'} = $self->lookup_product($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Plan Type'){ + $row->{'oldvalue'} = lookup_type($row->{'oldvalue'}); + $row->{'newvalue'} = lookup_type($row->{'newvalue'}); + } + } + return $ref; +} + +sub copy_permissions { + my $self = shift; + my ($planid) = @_; + my $dbh = Bugzilla->dbh; + + my ($regexp, $perms) = $dbh->selectrow_array( + "SELECT user_regexp, permissions + FROM test_plan_permissions_regexp + WHERE plan_id = ?",undef, $self->id); + + $dbh->do("INSERT INTO test_plan_permissions_regexp (plan_id, user_regexp, permissions) + VALUES(?,?,?)", undef,($planid, $regexp, $perms)) if $regexp; + + my $ref = $dbh->selectall_arrayref( + "SELECT userid, permissions + FROM test_plan_permissions + WHERE plan_id = ? AND grant_type = ?", + {'Slice' =>{}}, ($self->id, GRANT_DIRECT)); + foreach my $row (@$ref){ + $dbh->do("INSERT INTO test_plan_permissions (userid, plan_id, permissions, grant_type) + VALUES(?,?,?,?)", undef, ($row->{'userid'}, $planid, $row->{'permissions'}, GRANT_DIRECT)); + } +} + +=head2 lookup_type + +Takes an ID of the type field and returns the value + +=cut + +sub lookup_type { + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM test_plan_types + WHERE type_id = ?", + undef, $id); + return $value; +} + +=head2 lookup_type_by_name + +Returns the id of the type name passed. + +=cut + +sub lookup_type_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT type_id + FROM test_plan_types + WHERE name = ?", + undef, $name); + return $value; +} + +=head2 lookup_product + +Takes an ID of the status field and returns the value + +=cut + +sub lookup_product { + my $self = shift; + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM products + WHERE id = ?", + undef, $id); + return $value; +} + +=head2 lookup_product_by_name + +Returns the id of the product name passed. + +=cut + +sub lookup_product_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + + # TODO 2.22 use Product.pm + my ($value) = $dbh->selectrow_array( + "SELECT id + FROM products + WHERE name = ?", + undef, $name); + return $value; +} + +sub set_tester_regexp { + my $self = shift; + my ($regexp, $permissions) = @_; + my $dbh = Bugzilla->dbh; + + ThrowUserError("invalid_regexp") unless (eval {qr/$regexp/}); + + return unless $regexp; + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM profiles + WHERE login_name REGEXP(?)", + undef, $regexp); + ThrowUserError("testopia-regexp-too-inclusive") if $count > Bugzilla->params->{'testopia-max-allowed-plan-testers'}; + + my ($is, $oldreg, $oldperms) = $dbh->selectrow_array( + "SELECT 1, user_regexp, permissions + FROM test_plan_permissions_regexp + WHERE plan_id = ?",undef, $self->id); + + return unless ($oldreg ne $regexp || $oldperms != $permissions); + if ($is){ + $dbh->do("UPDATE test_plan_permissions_regexp + SET user_regexp = ?, permissions = ? + WHERE plan_id = ?", undef, ($regexp, $permissions, $self->id)); + } + else { + $dbh->do("INSERT INTO test_plan_permissions_regexp + (plan_id, user_regexp, permissions) + VALUES(?,?,?)", + undef, ($self->id, $regexp, $permissions)); + } + + $self->derive_regexp_testers($regexp); + +} + +sub derive_regexp_testers { + my $self = shift; + my $regexp = shift; + ThrowUserError("invalid_regexp") unless (eval {qr/$regexp/}); + my $dbh = Bugzilla->dbh; + # Get the permissions of the regexp testers so we can set it later. + my ($permissions) = $dbh->selectrow_array( + "SELECT permissions + FROM test_plan_permissions_regexp + WHERE plan_id = ?", undef, $self->id); + + my $sth = $dbh->prepare("SELECT profiles.userid, profiles.login_name, plan_id + FROM profiles + LEFT JOIN test_plan_permissions + ON test_plan_permissions.userid = profiles.userid + AND test_plan_permissions.plan_id = ? + AND grant_type = ?"); + my $plan_add = $dbh->prepare("INSERT INTO test_plan_permissions + (userid, plan_id, permissions, grant_type) + VALUES (?,?,?,?)"); + my $plan_update = $dbh->prepare("UPDATE test_plan_permissions + SET permissions = ? + WHERE userid = ? AND plan_id = ? AND grant_type = ?"); + my $plan_del = $dbh->prepare("DELETE FROM test_plan_permissions + WHERE userid = ? AND plan_id = ? + AND grant_type = ?"); + $sth->execute($self->id, GRANT_REGEXP); + while (my ($userid, $login, $present) = $sth->fetchrow_array()) { + if (($regexp =~ /\S+/) && ($login =~ m/$regexp/i)){ + if ($present){ + $plan_update->execute($permissions, $userid, $self->id, GRANT_REGEXP) + } + else { + $plan_add->execute($userid, $self->id, $permissions, GRANT_REGEXP) + } + } + else { + $plan_del->execute($userid, $self->id, GRANT_REGEXP) if $present; + } + } + +} + +sub remove_tester { + my $self = shift; + my ($userid) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM test_plan_permissions + WHERE userid = ? AND plan_id = ? AND grant_type = ?", + undef, ($userid, $self->id, GRANT_DIRECT)); +} + +sub add_tester { + my $self = shift; + my ($userid, $perms) = @_; + my $dbh = Bugzilla->dbh; + my ($is) = $dbh->selectrow_array( + "SELECT userid + FROM test_plan_permissions + WHERE userid = ? AND plan_id = ? AND grant_type = ?", + undef, ($userid, $self->id, GRANT_DIRECT)); + return if $is; + + $dbh->do("INSERT INTO test_plan_permissions + (userid, plan_id, permissions, grant_type) + VALUES(?,?,?,?)", + undef, ($userid, $self->id, $perms, GRANT_DIRECT)); +} + +sub update_tester { + my $self = shift; + my ($userid, $perms) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->do("UPDATE test_plan_permissions SET permissions = ? + WHERE userid = ? AND plan_id = ? AND grant_type = ?", + undef, ($perms, $userid, $self->id, GRANT_DIRECT)); +} + +=head2 obliterate + +Removes this plan and all things that reference it. + +=cut + +sub obliterate { + my $self = shift; + my ($cgi, $template) = @_; + my $vars; + my $dbh = Bugzilla->dbh; + + my $progress_interval = 250; + my $i = 0; + my $total = scalar @{$self->test_cases} + scalar @{$self->test_runs}; + + foreach my $obj (@{$self->attachments}){ + $obj->obliterate; + } + foreach my $obj (@{$self->test_runs}){ + $obj->obliterate($cgi, $template); + } + foreach my $obj (@{$self->test_cases}){ + $i++; + if ($cgi && $i % $progress_interval == 0){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $vars->{'process'} = "Deleting test cases"; + + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + $obj->obliterate if (scalar @{$obj->plans} == 1); + } + + $dbh->do("DELETE FROM test_plan_texts WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_plan_tags WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_plan_activity WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_plan_permissions WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_plan_permissions_regexp WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_case_plans WHERE plan_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_plans WHERE plan_id = ?", undef, $self->id); + return 1; +} + +=head2 canview + +Returns true if the logged in user has rights to view this plan + +=cut + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->get_user_rights(Bugzilla->user->id) & TR_READ; + return 0; +} + +=head2 canedit + +Returns true if the logged in user has rights to edit this plan + +=cut + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->get_user_rights(Bugzilla->user->id) & TR_WRITE; + return 0; + +} + +=head2 candelete + +Returns true if the logged in user has rights to delete this plan + +=cut + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 0 unless Bugzilla->params->{"allow-test-deletion"}; + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + return 1 if $self->get_user_rights(Bugzilla->user->id) & TR_DELETE; + return 0; +} + +sub canadmin { + my $self = shift; + return 1 if Bugzilla->user->in_group("admin"); + return 1 if ($self->get_user_rights(Bugzilla->user->id) & TR_ADMIN); + return 0; +} + +sub get_user_rights { + my $self = shift; + my ($userid) = @_; + + my $dbh = Bugzilla->dbh; + my ($perms) = $dbh->selectrow_array( + "SELECT permissions FROM test_plan_permissions + WHERE userid = ? AND plan_id = ?", + undef, ($userid, $self->id)); + + return $perms || 0; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + + $obj->{'product_name'} = $self->product->name if $self->product; + $obj->{'run_count'} = $self->test_run_count; + $obj->{'case_count'} = $self->test_case_count; + $obj->{'author_name'} = $self->author->login; + $obj->{'plan_type'} = $self->plan_type; + $obj->{'canedit'} = $self->canedit; + $obj->{'canview'} = $self->canview; + $obj->{'candelete'} = $self->candelete; + $obj->{'link_url'} = 'tr_show_plan.cgi?plan_id=' . $self->id; + $obj->{'type'} = $self->type; + $obj->{'id'} = $self->id; + + return $json->encode($obj); +} + +############################### +#### Accessors #### +############################### +=head1 ACCESSOR METHODS + +=head2 id + +Returns the ID for this object + +=head2 creation_date + +Returns the creation timestamp for this object + +=head2 product_version + +Returns the product version for this object + +=head2 product_id + +Returns the product id for this object + +=head2 author + +Returns a Bugzilla::User object representing the plan author + +=head2 name + +Returns the name of this plan + +=head2 type_id + +Returns the type id of this plan + +=head2 isactive + +Returns true if this plan is not archived + +=cut + +sub id { return $_[0]->{'plan_id'}; } +sub creation_date { return $_[0]->{'creation_date'}; } +sub product_version { return $_[0]->{'default_product_version'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub author { return Bugzilla::User->new($_[0]->{'author_id'}); } +sub name { return $_[0]->{'name'}; } +sub type_id { return $_[0]->{'type_id'}; } +sub isactive { return $_[0]->{'isactive'}; } + +=head2 type + +Returns 'case' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'plan'; + return $self->{'type'}; +} + +sub tester_regexp { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + my ($regexp) = $dbh->selectrow_array( + "SELECT user_regexp + FROM test_plan_permissions_regexp + WHERE plan_id = ?", undef, $self->id); + + return $regexp; +} + +sub tester_regexp_permissions { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + my ($perms) = $dbh->selectrow_array( + "SELECT permissions + FROM test_plan_permissions_regexp + WHERE plan_id = ?", undef, $self->id); + my $p; + + $p->{'read'} = $perms >= TR_READ; + $p->{'write'} = $perms >= TR_WRITE; + $p->{'delete'} = $perms >= TR_DELETE; + $p->{'admin'} = $perms >= TR_ADMIN; + + return $p; +} + +sub access_list { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectall_arrayref( + "SELECT tpp.userid, permissions + FROM test_plan_permissions AS tpp + JOIN profiles ON profiles.userid = tpp.userid + WHERE plan_id = ? AND grant_type = ? + ORDER BY profiles.realname", {'Slice' =>{}}, ($self->id, GRANT_DIRECT)); + my @rows; + foreach my $row (@$ref){ + push @rows, {'user' => Bugzilla::User->new($row->{'userid'}), + 'read' => $row->{'permissions'} >= TR_READ, + 'write' => $row->{'permissions'} >= TR_WRITE, + 'delete' => $row->{'permissions'} >= TR_DELETE, + 'admin' => $row->{'permissions'} >= TR_ADMIN, + }; + } + $self->{'access_list'} = \@rows; + return $self->{'access_list'}; +} + +sub has_admin { + my ($self, $deleted) = @_; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT userid + FROM test_plan_permissions + WHERE plan_id = ? + AND userid != ? + AND permissions >= ?",undef, + $self->id, $deleted, TR_ADMIN); + + return scalar @$ref; +} + +=head2 attachments + +Returns a reference to a list of attachments on this plan + +=cut + +sub attachments { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'attachments'} if exists $self->{'attachments'}; + + my $attachments = $dbh->selectcol_arrayref( + "SELECT attachment_id + FROM test_plan_attachments + WHERE plan_id = ?", + undef, $self->{'plan_id'}); + + my @attachments; + foreach my $attach (@{$attachments}){ + push @attachments, Bugzilla::Testopia::Attachment->new($attach); + } + $self->{'attachments'} = \@attachments; + return $self->{'attachments'}; + +} + +=head2 bugs + +Returns a reference to a list of Bugzilla::Bug objects associated +with this plan + +=cut + +sub bugs { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'bugs'} if exists $self->{'bugs'}; + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT bug_id + FROM test_case_bugs + JOIN test_cases ON test_case_bugs.case_id = test_cases.case_id + JOIN test_case_plans ON test_case_plans.case_id = test_cases.case_id + WHERE test_case_plans.plan_id = ?", + undef, $self->id); + my @bugs; + foreach my $id (@{$ref}){ + push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id); + } + $self->{'bugs'} = \@bugs if @bugs; + $self->{'bug_list'} = join(',', @$ref); + return $self->{'bugs'}; +} + +=head2 product + +Returns the product this plan is associated with + +=cut + +sub product { + my ($self) = @_; + + return $self->{'product'} if exists $self->{'product'}; + + $self->{'product'} = Bugzilla::Testopia::Product->new($self->product_id); + return $self->{'product'}; +} + +=head2 test_cases + +Returns a reference to a list of Testopia::TestCase objects linked +to this plan + +=cut + +sub test_cases { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'test_cases'} if exists $self->{'test_cases'}; + + require Bugzilla::Testopia::TestCase; + + my $caseids = $dbh->selectcol_arrayref( + "SELECT case_id FROM test_case_plans + WHERE plan_id = ?", + undef, $self->{'plan_id'}); + my @cases; + foreach my $id (@{$caseids}){ + push @cases, Bugzilla::Testopia::TestCase->new($id); + } + + $self->{'test_cases'} = \@cases; + return $self->{'test_cases'}; +} + +=head2 test_case_count + +Returns a count of the test cases linked to this plan + +=cut + +sub test_case_count { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'test_case_count'} if exists $self->{'test_case_count'}; + $self->{'test_case_count'} = $dbh->selectrow_array( + "SELECT COUNT(case_id) FROM test_case_plans + WHERE plan_id = ?", + undef, $self->{'plan_id'}) || 0; + return $self->{'test_case_count'}; +} + +=head2 test_runs + +Returns a reference to a list of test runs in this plan + +=cut + +sub test_runs { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'test_runs'} if exists $self->{'test_runs'}; + + my $runids = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs + WHERE plan_id = ?", + undef, $self->{'plan_id'}); + my @runs; + foreach my $id (@{$runids}){ + push @runs, Bugzilla::Testopia::TestRun->new($id); + } + + $self->{'test_runs'} = \@runs; + return $self->{'test_runs'}; +} + +=head2 test_run_count + +Returns a count of the test cases linked to this plan + +=cut + +sub test_run_count { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'test_run_count'} if exists $self->{'test_run_count'}; + $self->{'test_run_count'} = $dbh->selectrow_array( + "SELECT COUNT(run_id) FROM test_runs + WHERE plan_id = ?", + undef, $self->{'plan_id'}) || 0; + return $self->{'test_run_count'}; +} + +sub test_case_run_count { + my $self = shift; + my ($status_id) = @_; + my $dbh = Bugzilla->dbh; + my $query = + "SELECT count(case_run_id) FROM test_case_runs + INNER JOIN test_runs ON test_case_runs.run_id = test_runs.run_id + INNER JOIN test_plans ON test_runs.plan_id = test_plans.plan_id + WHERE test_case_runs.iscurrent = 1 AND test_plans.plan_id = ?"; + $query .= " AND test_case_runs.case_run_status_id = ?" if $status_id; + my $count; + if ($status_id){ + ($count) = $dbh->selectrow_array($query,undef,($self->id,$status_id)); + } + else { + ($count) = $dbh->selectrow_array($query,undef,$self->id); + } + + return $count; +} + +sub builds_seen { + my $self = shift; + my ($status_id) = @_; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::Build; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT test_case_runs.build_id + FROM test_case_runs + INNER JOIN test_runs ON test_case_runs.run_id = test_runs.run_id + WHERE test_runs.plan_id = ? AND test_case_runs.iscurrent = 1", + undef,$self->id); + + my @o; + foreach my $id (@$ref){ + push @o, Bugzilla::Testopia::Build->new($id); + } + return \@o; +} + +sub environments_seen { + my $self = shift; + my ($status_id) = @_; + my $dbh = Bugzilla->dbh; + + require Bugzilla::Testopia::Environment; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT test_case_runs.environment_id + FROM test_case_runs + INNER JOIN test_runs ON test_case_runs.run_id = test_runs.run_id + WHERE test_runs.plan_id = ? AND test_case_runs.iscurrent = 1", + undef,$self->id); + + my @o; + foreach my $id (@$ref){ + push @o, Bugzilla::Testopia::Environment->new($id); + } + return \@o; +} + +=head2 tags + +Returns a reference to a list of Testopia::TestTag objects +associated with this plan + +=cut + +sub tags { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'tags'} if exists $self->{'tags'}; + my $tagids = $dbh->selectcol_arrayref("SELECT test_plan_tags.tag_id + FROM test_plan_tags + INNER JOIN test_tags ON test_plan_tags.tag_id = test_tags.tag_id + WHERE plan_id = ? + ORDER BY test_tags.tag_name", + undef, $self->{'plan_id'}); + my @plan_tags; + foreach my $t (@{$tagids}){ + push @plan_tags, Bugzilla::Testopia::TestTag->new($t); + } + $self->{'tags'} = \@plan_tags; + return $self->{'tags'}; +} + +=head2 text + +Returns the text of the plan document from the latest version +in the test_plan_texts table + +=cut + +sub text { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($version) = @_; + trick_taint($version) if $version; + return $self->{'text'} if exists $self->{'text'} && !$version; + + $version = $version || $self->version; + + my $text = $dbh->selectrow_hashref( + "SELECT plan_text, profiles.realname AS author, plan_text_version AS version + FROM test_plan_texts AS tpt + INNER JOIN profiles ON tpt.who = profiles.userid + WHERE plan_id = ? AND plan_text_version = ?", + undef, ($self->{'plan_id'}, $version)); + + return $text if scalar @_; + + $self->{'text'} = $text; + + return $self->{'text'}; +} + + +=head2 version + +Returns the plan text version. This number is incremented any time +changes are made to the plan document. + +=cut + +sub version { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'version'} if exists $self->{'version'}; + my ($ver) = $dbh->selectrow_array("SELECT MAX(plan_text_version) + FROM test_plan_texts + WHERE plan_id = ?", + undef, $self->{'plan_id'}); + + $self->{'version'} = $ver; + return $self->{'version'}; + + +} + +=head2 type + +Returns the type of this plan + +=cut + +sub plan_type { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'plan_type'} if exists $self->{'plan_type'}; + my ($type) = $dbh->selectrow_array("SELECT name + FROM test_plan_types + WHERE type_id = ?", + undef, $self->{'type_id'}); + + $self->{'plan_type'} = $type; + return $self->{'plan_type'}; +} + +=head1 TODO + +Use Bugzilla::Product and Version in 2.22 + +=head1 SEE ALSO + +Testopia::(TestRun, TestCase, Category, Build, Util) + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::TestPlan - Testopia Test Plan object + +=head1 DESCRIPTION + +This module represents a test plan in Testopia. The test plan +is the glue of testopia. Virtually all other objects associate +to a plan. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::TestPlan; + + $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + $plan = Bugzilla::Testopia::TestPlan->new({}); + +=cut + +=head2 new + +Instantiate a new test plan. This takes a single argument +either a test plan ID or a reference to a hash containing keys +identical to a test plan's fields and desired values. + +=cut diff --git a/Bugzilla/Testopia/TestRun.pm b/Bugzilla/Testopia/TestRun.pm new file mode 100644 index 0000000..74cf776 --- /dev/null +++ b/Bugzilla/Testopia/TestRun.pm @@ -0,0 +1,1513 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Ed Fuentetaja +# Joel Smith + +package Bugzilla::Testopia::TestRun; + +use strict; + +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Bug; +use Bugzilla::Config; + +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Build; + +use JSON; +use Date::Parse; +use base qw(Exporter Bugzilla::Object); + +@Bugzilla::Testopia::TestRun::EXPORT = qw(calculate_percent); + +############################### +#### Initialization #### +############################### + +use constant DB_TABLE => "test_runs"; +use constant NAME_FIELD => "summary"; +use constant ID_FIELD => "run_id"; +use constant DB_COLUMNS => qw( + run_id + plan_id + environment_id + product_version + build_id + plan_text_version + manager_id + start_date + stop_date + summary + notes + target_pass + target_completion +); + +sub report_columns { + my $self = shift; + my %columns; + # Changes here need to match Report.pm + $columns{'Status'} = "run_status"; + $columns{'Version'} = "default_product_version"; + $columns{'Product'} = "product"; + $columns{'Build'} = "build"; + $columns{'Milestone'} = "milestone"; + $columns{'Environment'} = "environment"; + $columns{'Tags'} = "tags"; + $columns{'Manager'} = "manager"; + my @result; + push @result, {'name' => $_, 'id' => $columns{$_}} foreach (sort(keys %columns)); + unshift @result, {'name' => '', 'id'=> ''}; + return \@result; + +} + +use constant REQUIRED_CREATE_FIELDS => qw(plan_id environment_id build_id + product_version summary manager_id + plan_text_version); + +use constant UPDATE_COLUMNS => qw(environment_id build_id product_version + summary manager_id plan_text_version notes + stop_date target_pass target_completion); + +use constant VALIDATORS => { + plan_id => \&_check_plan, + environment_id => \&_check_env, + build_id => \&_check_build, + summary => \&_check_summary, + manager_id => \&_check_manager, + plan_text_version => \&_check_plan_text_version, + notes => \&_check_notes, + target_pass => \&_check_target, + target_completion => \&_check_target, +}; + +############################### +#### Validators #### +############################### +sub _check_plan { + my ($invocant, $plan_id) = @_; + trick_taint($plan_id); + ThrowUserError('testopia-missing-required-field', {'field' => 'plan'}) unless $plan_id; + Bugzilla::Testopia::Util::validate_test_id($plan_id, 'plan'); + return $plan_id; +} + +sub _check_env { + my ($invocant, $env_id) = @_; + trick_taint($env_id); + ThrowUserError('testopia-missing-required-field', {'field' => 'environment'}) unless $env_id; + Bugzilla::Testopia::Util::validate_test_id($env_id, 'environment'); + return $env_id; +} + +sub _check_build { + my ($invocant, $build_id) = @_; + trick_taint($build_id); + ThrowUserError('testopia-missing-required-field', {'field' => 'build'}) unless $build_id; + Bugzilla::Testopia::Util::validate_test_id($build_id, 'build'); + return $build_id; +} + +sub _check_product_version { + my ($invocant, $version, $product) = @_; + if (ref $invocant){ + $product = $invocant->plan->product; + } + $version = trim($version); + trick_taint($version); + $version = Bugzilla::Version->check({product => $product, name => $version}); + return $version->name; +} + +sub _check_summary{ + my ($invocant, $summary) = @_; + $summary = clean_text($summary) if $summary; + trick_taint($summary); + if (!defined $summary || $summary eq '') { + ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}); + } + return $summary; +} + +sub _check_manager { + my ($invocant, $login) = @_; + $login = trim($login); + ThrowUserError('testopia-missing-required-field', {'field' => 'manager'}) unless $login; + if ($login =~ /^\d+$/){ + $login = Bugzilla::User->new($login); + return $login->id; + } + else { + my $id = login_to_id($login, THROW_ERROR); + return $id; + } +} + +#TODO: Check that version is in plan versions +sub _check_plan_text_version { + my ($invocant, $version) = @_; + trick_taint($version); + ThrowUserError('testopia-missing-required-field', {'field' => 'plan_version'}) unless $version; + return $version; +} + +sub _check_notes { + my ($invocant, $notes) = @_; + trick_taint($notes); + $notes = trim($notes); + $notes =~ s/\n$//; + return $notes; +} + +sub _check_target { + my ($invocant, $target) = @_; + detaint_natural($target); + return unless $target; + ThrowUserError('invalid_target') unless $target >= 0 && $target <= 100; + return $target; +} + +############################### +#### Mutators #### +############################### +sub set_environment { $_[0]->set('environment_id', $_[1]); } +sub set_build { $_[0]->set('build_id', $_[1]); } +sub set_summary { $_[0]->set('summary', $_[1]); } +sub set_manager { $_[0]->set('manager_id', $_[1]); } +sub set_plan_text_version { $_[0]->set('plan_text_version', $_[1]); } +sub set_notes { $_[0]->set('notes', $_[1]); } +sub set_stop_date { $_[0]->set('stop_date', $_[1]); } +sub set_target_pass { $_[0]->set('target_pass', $_[1]); } +sub set_target_completion { $_[0]->set('target_completion', $_[1]); } + +sub set_product_version { + my ($self, $value) = @_; + $value = $self->_check_product_version($value); + $self->set('product_version', $value); +} + +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + # We want to be able to supply an empty object to the templates for numerous + # lists etc. This is much cleaner than exporting a bunch of subroutines and + # adding them to $vars one by one. Probably just Laziness shining through. + if (ref $param eq 'HASH'){ + bless($param, $class); + return $param; + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub run_create_validators { + my $class = shift; + my $params = $class->SUPER::run_create_validators(@_); + my $plan = Bugzilla::Testopia::TestPlan->new($params->{plan_id}); + + $params->{product_version} = $class->_check_product_version($params->{product_version}, $plan->product); + + return $params; +} + +sub create { + my ($class, $params) = @_; + require Bugzilla::Testopia::TestPlan; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->run_create_validators($params); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + $field_values->{start_date} = $timestamp; + $field_values->{stop_date} = Bugzilla::Testopia::Util::get_time_stamp() if $field_values->{status} == 0; + delete $field_values->{status}; + + my $self = $class->SUPER::insert_create_data($field_values); + + return $self; +} + +sub update { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + $dbh->bz_start_transaction(); + + my $changed = $self->SUPER::update(); + + foreach my $field (keys %$changed){ + Bugzilla::Testopia::Util::log_activity('run', $self->id, $field, $timestamp, $changed->{$field}->[0], $changed->{$field}->[1]); + } + + $dbh->bz_commit_transaction(); +} + +############################### +#### Methods #### +############################### + +=head2 calculate_percent_completed + +Calculates a percentage from two numbers. Takes the total number +of IDLE case runs and the number of those that have another status +and adds them to get a total then takes the percentage. + +=cut + +sub calculate_percent { + my ($total, $count) = (@_); + my $percent; + if ($total == 0) { + $percent = 0; + } else { + $percent = $count*100/$total; + $percent = int($percent + 0.5); + if (($percent == 100) && ($count != $total)) { + #I don't want to see 100% unless every test is run + $percent = 99; + } + } + return $percent; +} + +=head2 add_cc + +Adds a user to the CC list for this run + +=cut + +sub add_cc{ + my $self = shift; + my ($ccid) = (@_); + my $dbh = Bugzilla->dbh; + $dbh->do("INSERT INTO test_run_cc(run_id, who) + VALUES (?,?)", undef, $self->{'run_id'}, $ccid); + #TODO: send mail + return 1; +} + +=head2 remove_cc + +Removes a user from the CC list of this run + +=cut + +sub remove_cc{ + my $self = shift; + my ($ccid) = (@_); + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_run_cc + WHERE run_id=? AND who=?", + undef, $self->{'run_id'}, $ccid); + #TODO: send mail + return 1; +} + +=head2 add_tag + +Associates a tag with this test run + +=cut + +sub add_tag { + my $self = shift; + my $dbh = Bugzilla->dbh; + my @tags; + foreach my $t (@_){ + if (ref $t eq 'ARRAY'){ + push @tags, $_ foreach @$t; + } + else{ + push @tags, split(',', $t); + } + } + + foreach my $name (@tags){ + my $tag = Bugzilla::Testopia::TestTag->create({'tag_name' => $name}); + $tag->attach($self); + } +} + +=head2 remove_tag + +Disassociates a tag from this test run + +=cut + +sub remove_tag { + my $self = shift; + my ($tag_name) = @_; + my $tag = Bugzilla::Testopia::TestTag->check_tag($tag_name); + ThrowUserError('testopia-unknown-tag', {'name' => $tag}) unless $tag; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_run_tags + WHERE tag_id=? AND run_id=?", + undef, $tag->id, $self->{'run_id'}); + return; +} + +=head2 add_case_run + +Associates a test case with this run by adding a new row to +the test_case_runs table + +=cut + +sub add_case_run { + my $self = shift; + my ($case_id, $sortkey, $status) = @_; + $status ||=IDLE; + trick_taint($case_id); + return 0 if $self->check_case($case_id); + my $case = Bugzilla::Testopia::TestCase->new($case_id); + $sortkey = $case->sortkey unless $sortkey; + + return 0 if $case->status ne 'CONFIRMED'; + my $assignee = $case->default_tester ? $case->default_tester->id : undef; + my $caserun = Bugzilla::Testopia::TestCaseRun->create({ + 'run_id' => $self->{'run_id'}, + 'case_id' => $case_id, + 'assignee' => $assignee, + 'case_text_version' => $case->version, + 'build_id' => $self->build->id, + 'environment_id' => $self->environment_id, + 'case_run_status_id' => $status, + 'sortkey' => $sortkey, + }); + return 1; +} + +=head2 store + +Stores a test run object in the database. This method is used to store a +newly created test run. It returns the new ID. + +=cut + +sub store { + my $self = shift; + my $dbh = Bugzilla->dbh; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'run_id'} DB_COLUMNS); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + $dbh->do("INSERT INTO test_runs ($columns) VALUES (?,?,?,?,?,?,?,?,?,?)", + undef, ($self->{'plan_id'}, $self->{'environment_id'}, + $self->{'product_version'}, $self->{'build_id'}, + $self->{'plan_text_version'}, $self->{'manager_id'}, + $timestamp, undef, $self->{'summary'}, $self->{'notes'})); + my $key = $dbh->bz_last_key( 'test_runs', 'run_id' ); + return $key; +} + +=head2 update_notes + +Updates just the notes for this run + +=cut + +sub update_notes { + my $self = shift; + my ($notes) = @_; + my $dbh = Bugzilla->dbh; + $dbh->do("UPDATE test_runs + SET notes = ? WHERE run_id = ?", + undef, $notes, $self->{'run_id'}); +} + +=head2 clone + +Creates a copy of this test run. Accepts the summary of the new run +and the build id to use. + +=cut + +sub clone { + my $self = shift; + my ($summary, $manager, $plan_id, $build_id, $env_id) = @_; + my $dbh = Bugzilla->dbh; + # Exclude the auto-incremented field from the column list. + my $columns = join(", ", grep {$_ ne 'run_id'} DB_COLUMNS); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + $dbh->do("INSERT INTO test_runs ($columns) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", + undef, ($plan_id, $env_id, + $self->{'product_version'}, $build_id, + $self->{'plan_text_version'}, $manager, + $timestamp, undef, $summary, undef, + $self->{'target_pass'}, $self->{'target_completion'})); + my $key = $dbh->bz_last_key( 'test_runs', 'run_id' ); + return $key; +} + +=head2 history + +Returns a reference to a list of history entries from the +test_run_activity table. + +=cut + +sub history { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT defs.description AS what, + p.login_name AS who, a.changed, a.oldvalue, a.newvalue + FROM test_run_activity AS a + JOIN test_fielddefs AS defs ON a.fieldid = defs.fieldid + JOIN profiles AS p ON a.who = p.userid + WHERE a.run_id = ?", + {'Slice'=>{}}, $self->{'run_id'}); + + foreach my $row (@$ref){ + if ($row->{'what'} eq 'Environment'){ + $row->{'oldvalue'} = $self->lookup_environment($row->{'oldvalue'}); + $row->{'newvalue'} = $self->lookup_environment($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Default Build'){ + $row->{'oldvalue'} = $self->lookup_build($row->{'oldvalue'}); + $row->{'newvalue'} = $self->lookup_build($row->{'newvalue'}); + } + elsif ($row->{'what'} eq 'Manager'){ + $row->{'oldvalue'} = $self->lookup_manager($row->{'oldvalue'}); + $row->{'newvalue'} = $self->lookup_manager($row->{'newvalue'}); + } + } + return $ref; +} + +=head2 obliterate + +Removes this run and all things that reference it. + +=cut + +sub obliterate { + my $self = shift; + my ($cgi, $template) = @_; + my $dbh = Bugzilla->dbh; + my $vars; + + my $progress_interval = 500; + my $i = 0; + my $total = scalar @{$self->caseruns}; + + foreach my $obj (@{$self->caseruns}){ + $i++; + if ($cgi && $i % $progress_interval == 0){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $vars->{'process'} = "Deleting Run " . $self->id; + + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + $obj->obliterate; + } + + $dbh->do("DELETE FROM test_run_cc WHERE run_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_run_tags WHERE run_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_run_activity WHERE run_id = ?", undef, $self->id); + $dbh->do("DELETE FROM test_runs WHERE run_id = ?", undef, $self->id); + return 1; +} + +=head2 Check_case + +Checks if the given test case is already associated with this run + +=cut + +sub check_case { + my $self = shift; + my ($case_id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT case_run_id + FROM test_case_runs + WHERE case_id = ? AND run_id = ?", + undef, ($case_id, $self->{'run_id'})); + return $value; +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + my $rc = $self->case_run_count; + + $self->bugs(); + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + $obj->{'plan'} = { id => $self->plan->id, product_id => $self->plan->product_id} if $self->plan; + $obj->{'build'} = { id => $self->build->id, name => $self->build->name} if $self->build; + $obj->{'environment'} = { id => $self->environment->id, name => $self->environment->name} if $self->environment; + $obj->{'case_count'} = $self->case_run_count; + $obj->{'manager'} = { login_name => $self->manager->login, name => $self->manager->name} if $self->manager; + $obj->{'manager_name'} = $self->manager->name if $self->manager; + $obj->{'canedit'} = $self->canedit; + $obj->{'canview'} = $self->canview; + $obj->{'candelete'} = $self->candelete; + $obj->{'status'} = $self->stop_date ? 'STOPPED' : 'RUNNING'; + $obj->{'type'} = $self->type; + $obj->{'id'} = $self->id; + $obj->{'product_id'} = $self->plan->product_id if $self->plan; + $obj->{'passed_pct'} = $self->case_run_count(PASSED) / $rc if $rc; + $obj->{'failed_pct'} = $self->case_run_count(FAILED) / $rc if $rc; + $obj->{'blocked_pct'} = $self->case_run_count(BLOCKED) / $rc if $rc; + $obj->{'complete_pct'} = $self->percent_complete() . '%'; + $obj->{'bug_list'} = $self->{'bug_list'}; + + return $json->encode($obj); +} + +=head2 lookup_environment + +Takes an ID of the envionment field and returns the value + +=cut + +sub lookup_environment { + my $self = shift; + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM test_environments + WHERE environment_id = ?", + undef, $id); + return $value; +} + +=head2 lookup_environment_by_name + +Takes the name of an envionment and returns its id + +=cut + +sub lookup_environment_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environments + WHERE name = ?", + undef, $name); + return $value; +} + +=head2 lookup_build + +Takes an ID of the build field and returns the value + +=cut + +sub lookup_build { + my $self = shift; + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT name + FROM test_builds + WHERE build_id = ?", + undef, $id); + return $value; +} + +=head2 lookup_manager + +Takes an ID of the manager field and returns the value + +=cut + +sub lookup_manager { + my $self = shift; + my ($id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT login_name + FROM profiles + WHERE userid = ?", + undef, $id); + return $value; +} + +=head2 last_changed + +Returns the date of the last change in the history table + +=cut + +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_run_activity + WHERE run_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; +} + +sub filter_case_categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT tcc.category_id, tcc.name + FROM test_case_categories AS tcc + JOIN test_cases ON test_cases.category_id = tcc.category_id + JOIN test_case_runs AS tcr ON test_cases.case_id = tcr.case_id + WHERE run_id = ? + ORDER BY tcc.name", + undef, $self->id); + + my @categories; + foreach my $id (@$ids){ + push @categories, Bugzilla::Testopia::Category->new($id); + } + + return \@categories; +} + +sub filter_builds { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT test_case_runs.build_id, test_builds.name + FROM test_case_runs + INNER JOIN test_builds on test_builds.build_id = test_case_runs.build_id + WHERE run_id = ? + ORDER BY test_builds.name", + undef, $self->id); + + my @builds; + foreach my $id (@$ids){ + push @builds, Bugzilla::Testopia::Build->new($id); + } + return \@builds; +} + +sub filter_components { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT components.id, components.name + FROM components + JOIN test_case_components AS tcc ON tcc.component_id = components.id + JOIN test_cases ON test_cases.case_id = tcc.case_id + JOIN test_case_runs AS tcr ON test_cases.case_id = tcr.case_id + WHERE run_id = ? + ORDER BY components.name", + undef, $self->id); + + my @components; + foreach my $id (@$ids){ + push @components, Bugzilla::Component->new($id); + } + + return \@components; +} + +=head2 environments + +Returns a reference to a list of Testopia::Environment objects. + +=cut + +sub environments { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'environments'} if exists $self->{'environments'}; + + my $environments = + $dbh->selectcol_arrayref("SELECT environment_id + FROM test_environments"); + + my @environments; + foreach my $id (@{$environments}){ + push @environments, Bugzilla::Testopia::Environment->new($id); + } + $self->{'environments'} = \@environments; + return $self->{'environments'}; +} + +=head2 get_status_list + +Returns a list of statuses for a run + +=cut + +sub get_status_list { + my @status = ( + { 'id' => 1, 'name' => 'RUNNING' }, + { 'id' => 0, 'name' => 'STOPPED' }, + ); + return \@status; +} + + +=head2 get_fields + +Returns a reference to a list of test run field descriptions from +the test_fielddefs table. + +=cut + +sub get_fields { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $types = $dbh->selectall_arrayref( + "SELECT fieldid AS id, description AS name + FROM test_fielddefs + WHERE table_name=?", + {"Slice"=>{}}, "test_runs"); + unshift @$types, {id => '[Creation]', name => '[Started]'}; + return $types; +} + +=head2 get_distinct_builds + +Returns a list of build names for use in searches + +=cut + +sub get_distinct_builds { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $query = "SELECT build.name AS id, build.name " . + "FROM test_builds AS build " . + "JOIN products ON build.product_id = products.id " . + "LEFT JOIN group_control_map " . + "ON group_control_map.product_id = products.id "; + if (Bugzilla->params->{'useentrygroupdefault'}) { + $query .= "AND group_control_map.entry != 0 "; + } else { + $query .= "AND group_control_map.membercontrol = " . + CONTROLMAPMANDATORY . " "; + } + if (%{Bugzilla->user->groups}) { + $query .= "AND group_id NOT IN(" . + join(',', values(%{Bugzilla->user->groups})) . ") "; + } + $query .= "WHERE group_id IS NULL AND build.isactive = 1 ORDER BY build.name"; + + my $ref = $dbh->selectall_arrayref($query, {'Slice'=>{}}); + + return $ref; +} + +=head2 get_distinct_milestones + +Returns a list of milestones for use in searches + +=cut + +sub get_distinct_milestones { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT value AS id, value as name + FROM milestones + ORDER BY sortkey", {'Slice'=>{}}); + + return $ref; +} + +=head2 get_environments + +Returns a list of environments for use in searches + +=cut + +sub get_environments { + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT name AS id, name + FROM test_environments + ORDER BY name", + {'Slice'=>{}}); + + return $ref; +} + +=head2 canview + +Returns true if the logged in user has rights to view this test run. + +=cut + +sub canview { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->plan->get_user_rights(Bugzilla->user->id) & TR_READ; + return 0; + +} + +=head2 canedit + +Returns true if the logged in user has rights to edit this test run. + +=cut + +sub canedit { + my $self = shift; + return 1 if Bugzilla->user->in_group('Testers'); + return 1 if $self->plan->get_user_rights(Bugzilla->user->id) & TR_WRITE; + return 0; +} + +# Only certain people are able to change the status of a run. +sub canstatus { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 1 if $self->plan->get_user_rights(Bugzilla->user->id) & TR_ADMIN; + return 1 if $self->manager->id == Bugzilla->user->id; + return 0; +} + +=head2 candelete + +Returns true if the logged in user has rights to delete this test run. + +=cut + +sub candelete { + my $self = shift; + return 1 if Bugzilla->user->in_group('admin'); + return 0 unless Bugzilla->params->{"allow-test-deletion"}; + return 1 if Bugzilla->user->in_group('Testers') && Bugzilla->params->{"testopia-allow-group-member-deletes"}; + return 1 if $self->plan->get_user_rights(Bugzilla->user->id) & TR_DELETE; + return 0; +} + +sub completion_percent { + my $self = shift; + my ($products, $plans, $runs) = @_; + my $dbh = Bugzilla->dbh; + my @runs; + foreach my $p (@$products){ + foreach my $plan (@{$p->plans}){ + push @runs, $_->id foreach (@{$plan->test_runs}); + } + } + push @runs, $_->id foreach (@{$plans->test_runs}); + push @runs, $_->id foreach (@$runs); + return 0 unless scalar @runs; + + my $run_ids = join (',',@runs); + + +} + +sub total_time { + my $self = shift; + + return 0 unless $self->stop_date; + + my $seconds = str2time($self->stop_date) - str2time($self->start_date); + + my @time = gmtime($seconds); + my %time; + + $time{day} = $time[7]; + $time{hr} = $time[2]; + $time{min} = $time[1]; + $time{sec} = $time[0]; + + return $time{day}.":".$time{hr}.":".$time{min}.":".$time{sec}; +} + +############################### +#### Accessors #### +############################### + +=head1 ACCESSOR METHODS + +=head2 id + +Returns the ID for this object + +=head2 plan_text_version + +Returns the plan's text version of this run + +=head2 plan_id + +Returns the plan idof this run + +=head2 environment_id + +Returns the environment id of this run + +=head2 manager + +Returns a Bugzilla::User object representing the run's manager + +=head2 start_date + +Returns the time stamp of when this run was started + +=head2 stop_date + +Returns the time stamp of when this run was completed + +=head2 summary + +Returns the summary of this run + +=head2 notes + +Returns the notes for this run + +=head2 product_version + +Returns the product version of this run + +=cut + +sub id { return $_[0]->{'run_id'}; } +sub plan_text_version { return $_[0]->{'plan_text_version'}; } +sub plan_id { return $_[0]->{'plan_id'}; } +sub environment_id { return $_[0]->{'environment_id'}; } +sub manager { return Bugzilla::User->new($_[0]->{'manager_id'}); } +sub start_date { return $_[0]->{'start_date'}; } +sub stop_date { return $_[0]->{'stop_date'}; } +sub summary { return $_[0]->{'summary'}; } +sub notes { return $_[0]->{'notes'}; } +sub product_version { return $_[0]->{'product_version'}; } +sub target_pass { return $_[0]->{'target_pass'}; } +sub target_completion { return $_[0]->{'target_completion'}; } + +=head2 type + +Returns 'case' + +=cut + +sub type { + my $self = shift; + $self->{'type'} = 'run'; + return $self->{'type'}; +} + +=head2 plan + +Returns the Testopia::TestPlan object of the plan this run +is assoceated with + +=cut + +sub plan { + my $self = shift; + return $self->{'plan'} if exists $self->{'plan'}; + require Bugzilla::Testopia::TestPlan; + $self->{'plan'} = Bugzilla::Testopia::TestPlan->new($self->{'plan_id'}); + return $self->{'plan'}; +} + +=head2 tags + +Returns a reference to a list of Testopia::TestTag objects +associated with this run + +=cut + +sub tags { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'tags'} if exists $self->{'tags'}; + my $tagids = $dbh->selectcol_arrayref("SELECT test_run_tags.tag_id + FROM test_run_tags + INNER JOIN test_tags ON test_run_tags.tag_id = test_tags.tag_id + WHERE run_id = ? + ORDER BY test_tags.tag_name", + undef, $self->{'run_id'}); + my @tags; + foreach my $t (@{$tagids}){ + push @tags, Bugzilla::Testopia::TestTag->new($t); + } + $self->{'tags'} = \@tags; + return $self->{'tags'}; +} + +=head2 environment + +Returns the Testopia::Environment object of the environment +this run is assoceated with + +=cut + +sub environment { + my $self = shift; + return $self->{'environment'} if exists $self->{'environment'}; + $self->{'environment'} = Bugzilla::Testopia::Environment->new($self->{'environment_id'}); + return $self->{'environment'}; + +} + +=head2 build + +Returns the Testopia::Build object of the plan this run +is assoceated with + +=cut + +sub build { + my $self = shift; + return $self->{'build'} if exists $self->{'build'}; + $self->{'build'} = Bugzilla::Testopia::Build->new($self->{'build_id'}); + return $self->{'build'}; + +} + +=head2 runtime + +Returns the total time the run took to complete + +=cut + +sub runtime { + +} + +=head2 bugs + +Returns a reference to a list of Bugzilla::Bug objects associated +with this run + +=cut + +sub bugs { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'bugs'} if exists $self->{'bugs'}; + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT bug_id + FROM test_case_bugs b + JOIN test_case_runs r ON r.case_run_id = b.case_run_id + WHERE r.run_id = ? AND r.iscurrent = 1 ORDER BY bug_id", + undef, $self->{'run_id'}); + my @bugs; + foreach my $id (@{$ref}){ + push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id); + } + $self->{'bugs'} = \@bugs if @bugs; + $self->{'bug_list'} = join(',', @$ref); + return $self->{'bugs'}; +} + +=head2 cc + +Returns a reference to a list of Bugzilla::User objects +on the CC list of this run + +=cut + +sub cc { + my $self = shift; + return $self->{'cc'} if exists $self->{'cc'}; + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectcol_arrayref( + "SELECT who FROM test_run_cc + WHERE run_id=?", undef, $self->{'run_id'}); + my @cc; + foreach my $id (@{$ref}){ + push @cc, Bugzilla::User->new($id); + } + $self->{'cc'} = \@cc; + return $self->{'cc'}; +} + +=head2 cases + +Returns a reference to a list of Testopia::TestCase objects +associated with this run + +=cut + +sub cases { + my $self = shift; + return $self->{'cases'} if exists $self->{'cases'}; + my @cases; + foreach my $cr (@{$self->current_caseruns}){ + push @cases, Bugzilla::Testopia::TestCase->new($cr->case_id); + } + $self->{'cases'} = \@cases; + return $self->{'cases'}; + +} + +sub case_ids { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'case_ids'} if exists $self->{'case_ids'}; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT case_id FROM test_case_runs + WHERE run_id=? AND iscurrent=1", undef, + $self->{'run_id'}); + + $self->{'case_ids'} = $ref; + return $self->{'case_ids'}; + +} + +=head2 case_count + +Returns a count of the test cases associated with this run + +=cut + +sub case_count { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(case_run_id) FROM test_case_runs + WHERE run_id=? AND iscurrent=1", undef, + $self->{'run_id'}); + + return scalar $count; +} + +sub case_run_count { + my $self = shift; + my ($status_id, $runs, $plans, $products) = @_; + my $dbh = Bugzilla->dbh; + + my @runs; + if ($products){ + foreach my $p (@$products){ + foreach my $plan (@{$p->plans}){ + push @runs, $_->id foreach (@{$plan->test_runs}); + } + } + } + if ($plans){ + push @runs, $_->id foreach (@{$plans->test_runs}); + } + if ($runs){ + push @runs, $_->id foreach (@$runs); + } + push @runs, $self->id if $self->id; + + return 0 unless scalar @runs > 0; + + my $run_ids = join (',', @runs); + + my $query = + "SELECT COUNT(*) + FROM test_case_runs + WHERE run_id IN (" . $run_ids .") AND iscurrent = 1"; + $query .= " AND case_run_status_id = ?" if $status_id; + + my $count; + if ($status_id){ + ($count) = $dbh->selectrow_array($query,undef,($status_id)); + } + else { + ($count) = $dbh->selectrow_array($query); + } + + return $count; +} + +sub case_run_count_by_date { + my $self = shift; + my ($start, $stop, $status_id, $tester, $runs, $plans, $products) = @_; + my $dbh = Bugzilla->dbh; + + my @runs; + if ($products){ + foreach my $p (@$products){ + foreach my $plan (@{$p->plans}){ + push @runs, $_->id foreach (@{$plan->test_runs}); + } + } + } + if ($plans){ + push @runs, $_->id foreach (@{$plans->test_runs}); + } + if ($runs){ + push @runs, $_->id foreach (@$runs); + } + push @runs, $self->id if $self->id; + + return 0 unless scalar @runs > 0; + + my $run_ids = join (',', @runs); + + my $query = + "SELECT COUNT(*) + FROM test_case_runs + WHERE run_id IN (" . $run_ids .") + AND close_date >= ? + AND close_date <= ? + AND iscurrent = 1"; + $query .= " AND case_run_status_id = ?" if $status_id; + $query .= " AND testedby = $tester" if $tester; + + my $count; + if ($status_id){ + ($count) = $dbh->selectrow_array($query,undef,($start,$stop,$status_id)); + } + else { + ($count) = $dbh->selectrow_array($query,undef,($start,$stop)); + } + + return $count; +} + +sub case_run_count_by_priority { + my $self = shift; + my ($priority, $status_id, $runs, $plans, $products) = @_; + trick_taint($priority); + my $dbh = Bugzilla->dbh; + + my @runs; + if ($products){ + foreach my $p (@$products){ + foreach my $plan (@{$p->plans}){ + push @runs, $_->id foreach (@{$plan->test_runs}); + } + } + } + if ($plans){ + push @runs, $_->id foreach (@{$plans->test_runs}); + } + if ($runs){ + push @runs, $_->id foreach (@$runs); + } + push @runs, $self->id if $self->id; + + return 0 unless scalar @runs > 0; + + my $run_ids = join (',', @runs); + + my $query = + "SELECT COUNT(*) + FROM test_case_runs + INNER JOIN test_cases on test_case_runs.case_id = test_cases.case_id + WHERE run_id IN (" . $run_ids .") + AND test_cases.priority_id = ? + AND iscurrent = 1"; + $query .= " AND case_run_status_id = ?" if $status_id; + + my $count; + if ($status_id){ + ($count) = $dbh->selectrow_array($query,undef,($priority, $status_id)); + } + else { + ($count) = $dbh->selectrow_array($query,undef,($priority)); + } + + return $count; +} + +sub finished_count { + my $self = shift; + my ($status_id) = @_; + my $dbh = Bugzilla->dbh; + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM test_case_runs + WHERE run_id = ? AND iscurrent = 1 + AND case_run_status_id IN (?,?,?)",undef, ($self->id, FAILED, PASSED, BLOCKED)); + + return $count; +} + + + +=head2 percent_complete + +Returns a number representing the percentage of case-runs +that have a status vs. those with a status of IDLE + +=cut + +sub percent_complete { + my $self = shift; + return $self->{'percent_complete'} if defined $self->{'percent_complete'}; + $self->{'percent_complete'} = calculate_percent($self->case_run_count,$self->finished_count); + return $self->{'percent_complete'}; +} + +sub percent_of_total { + my $self = shift; + my ($status_id) = @_; + return calculate_percent($self->case_run_count,$self->case_run_count($status_id)); +} + +sub percent_of_finished { + my $self = shift; + my ($status_id) = @_; + return calculate_percent($self->finished_count,$self->case_run_count($status_id)); +} + +=head2 current_caseruns + +Returns a reference to a list of TestCaseRun objects that are the +current case-runs on this run + +=cut + +sub current_caseruns { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'current_caseruns'} if exists $self->{'current_caseruns'}; + + require Bugzilla::Testopia::TestCaseRun; + + my $ref = $dbh->selectcol_arrayref( + "SELECT case_run_id FROM test_case_runs + WHERE run_id=? AND iscurrent=1", undef, + $self->{'run_id'}); + my @caseruns; + + foreach my $id (@{$ref}){ + push @caseruns, Bugzilla::Testopia::TestCaseRun->new($id); + } + $self->{'current_caseruns'} = \@caseruns; + return $self->{'current_caseruns'}; +} + +=head2 caseruns + +Returns a reference to a list of TestCaseRun objects that belong +to this run + +=cut + +sub caseruns { + my $self = shift; + my $dbh = Bugzilla->dbh; + return $self->{'caseruns'} if exists $self->{'caseruns'}; + + require Bugzilla::Testopia::TestCaseRun; + + my $ref = $dbh->selectcol_arrayref( + "SELECT case_run_id FROM test_case_runs + WHERE run_id=?", undef, $self->{'run_id'}); + my @caseruns; + + foreach my $id (@{$ref}){ + push @caseruns, Bugzilla::Testopia::TestCaseRun->new($id); + } + $self->{'caseruns'} = \@caseruns; + return $self->{'caseruns'}; +} + +=head2 case_id_list + +Returns a list of case_id's from the current case runs. + +=cut + +sub case_id_list { + my $self = shift; + my @ids; + foreach my $c (@{$self->current_caseruns}){ + push @ids, $c->case_id; + } + + return join(",", @ids); +} + +=head1 SEE ALSO + +Testopia::(TestPlan, TestCase, Category, Build, Environment) + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::TestRun - Testopia Test Run object + +=head1 DESCRIPTION + +This module represents a test run in Testopia. A test run is the +place where most of the work of testing is done. A run is associated +with a single test plan and multiple test cases through the test +case-runs. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::TestRun; + + $run = Bugzilla::Testopia::TestRun->new($run_id); + $run = Bugzilla::Testopia::TestRun->new(\%run_hash); + +=cut + +=head1 FIELDS + + run_id + plan_id + environment_id + product_version + build_id + plan_text_version + manager_id + start_date + stop_date + summary + notes + +=cut + +=head2 new + +Instantiate a new Test Run. This takes a single argument +either a test run ID or a reference to a hash containing keys +identical to a test run's fields and desired values. + +=cut diff --git a/Bugzilla/Testopia/TestTag.pm b/Bugzilla/Testopia/TestTag.pm new file mode 100644 index 0000000..91f63f7 --- /dev/null +++ b/Bugzilla/Testopia/TestTag.pm @@ -0,0 +1,374 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +=head1 NAME + +Bugzilla::Testopia::TestTag - A Testopia tag + +=head1 DESCRIPTION + +Tags in Testopia are used to classify objects in an intuitive manner. +Tags are user defined strings that can be attached to Test Plans, test +Cases, and Test Runs. They are similar to keywords in Bugzilla but do +not require administrator privileges to create. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::TestTag; + + $tag = Bugzilla::Testopia::TestTag->new($tag_id); + $tag = Bugzilla::Testopia::TestTag->new($tag_hash); + +=cut + +package Bugzilla::Testopia::TestTag; + +use strict; + +use Bugzilla::Util; +use Bugzilla::Error; + +use JSON; + +use base qw(Exporter Bugzilla::Object); + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + tag_id + tag_name + +=cut +use constant DB_TABLE => "test_tags"; +use constant NAME_FIELD => "tag_name"; +use constant ID_FIELD => "tag_id"; +use constant DB_COLUMNS => qw( + tag_id + tag_name +); + +use constant REQUIRED_CREATE_FIELDS => qw(tag_name); +use constant UPDATE_COLUMNS => qw(); +use constant VALIDATORS => { + tag_name => \&_check_name, +}; + +############################### +#### Validators #### +############################### +sub _check_name { + my ($invocant, $name) = @_; + $name = trim($name); + trick_taint($name); + return $name; +} + + +=head1 METHODS + +=head2 new + +Instantiates a new TestTag + +=cut +############################### +#### Mutators #### +############################### +sub new { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $param = shift; + + if (ref $param eq 'HASH'){ + ThrowCodeError('non-empty-hash'); + } + + unshift @_, $param; + my $self = $class->SUPER::new(@_); + + return $self; +} + +sub create { + my ($class, $params) = @_; + my $dbh = Bugzilla->dbh; + + $class->SUPER::check_required_create_fields($params); + my $field_values = $class->SUPER::run_create_validators($params); + + my ($key) = $dbh->selectrow_array("SELECT tag_id FROM test_tags + WHERE LOWER(tag_name) = ?", + undef, lc($field_values->{'tag_name'})); + + return $class->new($key) if $key; + return $class->insert_create_data($field_values); +} + +############################### +#### Methods #### +############################### + +=head2 check_name + +Checks the supplied name to see if a tag of that name exists. +If it does it returns a TestTag object. Otherwise it returns +undef. + +=cut + +sub check_tag { + my $self = shift; + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($id) = $dbh->selectrow_array( + "SELECT tag_id FROM test_tags + WHERE tag_name = ?", + undef, lc($name)); + if ($id){ + return Bugzilla::Testopia::TestTag->new($id); + } + else{ + return undef; + } +} + +sub attach { + my $self = shift; + my ($obj) = shift; + my $dbh = Bugzilla->dbh; + $dbh->bz_start_transaction(); + my $tagged = $dbh->selectrow_array( + "SELECT 1 FROM test_". $obj->type ."_tags + WHERE tag_id = ? AND ". $obj->type ."_id = ?", + undef, $self->id, $obj->id); + if ($tagged) { + $dbh->bz_commit_transaction(); + return; + } + $dbh->do("INSERT INTO test_". $obj->type ."_tags(tag_id, ". $obj->type ."_id, userid) + VALUES(?,?,?)", + undef, $self->id, $obj->id, Bugzilla->user->id); + $dbh->bz_commit_transaction(); +} + +=head2 store + +Checks if the given tag exists in the database. If so, it returns +that ID. Otherwise it submits the tag to the database and returns +the newly created ID. + +=cut + +sub store { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $key; + $self->{'tag_name'} = trim($self->{'tag_name'}); + $dbh->bz_start_transaction(); + ($key) = $dbh->selectrow_array("SELECT tag_id FROM test_tags + WHERE LOWER(tag_name) = ?", + undef, lc($self->{'tag_name'})); + if ($key) { + $dbh->bz_commit_transaction(); + return $key; + } + $dbh->do("INSERT INTO test_tags (tag_name) VALUES (?)", + undef, ($self->{'tag_name'})); + $key = $dbh->bz_last_key( 'test_tags', 'tag_id' ); + $dbh->bz_commit_transaction(); + + $self->{'tag_id'} = $key; + return $key; +} + +=head2 obliterate + +Completely removes a tag from the database. This is the only safe +way to do this. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_case_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + $dbh->do("DELETE FROM test_plan_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + $dbh->do("DELETE FROM test_run_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + $dbh->do("DELETE FROM test_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); +} + +=head2 case_list + +Returns a comma separated list of case ids associated with this tag + +=cut + +sub case_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $list = $dbh->selectcol_arrayref( + "SELECT case_id FROM test_case_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + return join(",", @{$list}); +} + +=head2 plan_list + +Returns a comma separated list of plan ids associated with this tag + +=cut + +sub plan_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $list = $dbh->selectcol_arrayref( + "SELECT plan_id FROM test_plan_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + return join(",", @{$list}); +} + +=head2 run_list + +Returns a comma separated list of run ids associated with this tag + +=cut + +sub run_list { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $list = $dbh->selectcol_arrayref( + "SELECT run_id FROM test_run_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + return join(",", @{$list}); +} + +sub candelete { + my $self = shift; + return 0 unless Bugzilla->user->in_group("admin"); +} + +############################### +#### Accessors #### +############################### + +=head1 ACCCESSOR METHODS + +=head2 id + +Returns the tag ID + +=head2 name + +Returns the tag name + +=cut + +sub id { return $_[0]->{'tag_id'}; } +sub name { return $_[0]->{'tag_name'}; } + +=head2 case_count + +Returns a count of the test cases associated with this tag + +=cut + +sub case_count { + my $self = shift; + return $self->{'case_count'} if exists $self->{'case_count'}; + my $dbh = Bugzilla->dbh; + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(tag_id) + FROM test_case_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + $self->{'case_count'} = $count; + return $self->{'case_count'}; +} + +=head2 plan_count + +Returns a count of the test plans associated with this tag + +=cut + +sub plan_count { + my $self = shift; + return $self->{'plan_count'} if exists $self->{'plan_count'}; + my $dbh = Bugzilla->dbh; + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(tag_id) + FROM test_plan_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + + $self->{'plan_count'} = $count; + return $self->{'plan_count'}; + +} + +=head2 run_count + +Returns a count of the test runs associated with this tag + +=cut + +sub run_count { + my $self = shift; + return $self->{'run_count'} if exists $self->{'run_count'}; + my $dbh = Bugzilla->dbh; + my ($count) = $dbh->selectrow_array( + "SELECT COUNT(tag_id) + FROM test_run_tags + WHERE tag_id = ?", undef, $self->{'tag_id'}); + $self->{'run_count'} = $count; + return $self->{'run_count'} +} + +sub TO_JSON { + my $self = shift; + my $obj; + my $json = new JSON; + + foreach my $field ($self->DB_COLUMNS){ + $obj->{$field} = $self->{$field}; + } + $obj->{'run_count'} = $self->run_count; + $obj->{'plan_count'} = $self->plan_count; + $obj->{'case_count'} = $self->case_count; + + return $json->encode($obj); +} + +=head1 SEE ALSO + +TestPlan TestRun TestCase + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; diff --git a/Bugzilla/Testopia/Util.pm b/Bugzilla/Testopia/Util.pm new file mode 100644 index 0000000..00b0927 --- /dev/null +++ b/Bugzilla/Testopia/Util.pm @@ -0,0 +1,228 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Maciej Maczynski +# Ed Fuentetaja +# Greg Hendricks + +=head1 NAME + +Bugzilla::Testopia::Util + +=head1 DESCRIPTION + +This module contains miscillaneous functions used by Testopia. +It exports all of these functions using Exporter. + +=cut + +package Bugzilla::Testopia::Util; + +use strict; + +use base qw(Exporter); +@Bugzilla::Testopia::Util::EXPORT = qw(get_test_field_id get_time_stamp + validate_test_id validate_selection + support_server_push process_list + percentage); + +use Bugzilla; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Util; +#use Bugzilla::Testopia::TestPlan; + +### Methods ### + +=head2 get_test_field_id + +Takes a field name and table and returns the fieldid from the +test_fielddefs table. + +=cut + +sub get_test_field_id { + my ($field, $table) = @_; + my $dbh = Bugzilla->dbh; + + my ($field_id) = $dbh->selectrow_array( + "SELECT fieldid + FROM test_fielddefs + WHERE name = ? AND table_name = ?", + undef, $field, $table); + ThrowCodeError('testopia_undefined_field', {fieldname => $field, table => $table}) unless $field_id; + return $field_id; +} + +=head2 get_time_stamp + +Returns an SQL timestamp + +=cut + +sub get_time_stamp { + my $dbh = Bugzilla->dbh; + my ($timestamp) = $dbh->selectrow_array("SELECT NOW()"); + return $timestamp; +} + +=head2 tc_alias_to_id + +Takes a test case alias and returns the corresponding ID + +=cut + +sub tc_alias_to_id { + my ($alias) = @_; + my $dbh = Bugzilla->dbh; + trick_taint($alias); + return $dbh->selectrow_array( + "SELECT case_id FROM test_cases WHERE alias = ?", undef, $alias); +} + +=head2 validate_test_id + +Takes an ID and a Testopia type and validates it against the database. +In the case of cases it will validate and return an ID from an alias. +Much of this was taken from Bugzilla. This function assumes all tables +in the database are named test_s + +=cut + +sub validate_test_id { + my $dbh = Bugzilla->dbh; + my ($id, $type) = @_; + $id = trim($id); + + # If the ID isn't a number, it might be an alias, so try to convert it. + my $alias = $id; + if (!detaint_natural($id) && $type eq 'case') { + $id = tc_alias_to_id($alias); + $id || ThrowUserError("testopia-invalid-test-id-or-alias", + {'case_id' => $alias}); + } + + # Modify the calling code's original variable to contain the trimmed, + # converted-from-alias ID. + $_[0] = $id; + + # First check that the object exists + my ($res) = $dbh->selectrow_array("SELECT ". $type. "_id FROM test_". $type."s + WHERE ". $type ."_id=?",undef, $id); + + $res + || ThrowUserError("invalid-test-id-non-existent", + {'id' => $alias, 'type' => $type}); + return $res; +} + +=head2 validate_selection + +Checks that the selected option is a valid one + +=cut + +sub validate_selection { + my $dbh = Bugzilla->dbh; + my ($id, $field, $table) = @_; + $id = trim($id); + trick_taint($id); + + my ($res) = $dbh->selectrow_array( + "SELECT $field + FROM $table + WHERE $field = ?", + undef, $id); + + $res + || ThrowUserError("invalid-test-id-non-existent", + {'id' => $id, 'type' => $table}); + return $res; +} + +sub support_server_push { + my ($cgi) = @_; + my $serverpush = + exists $ENV{'HTTP_USER_AGENT'} + && $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/ + && $ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/ + && $ENV{'HTTP_USER_AGENT'} !~ /WebKit/ + && !defined($cgi->param('serverpush')) + || $cgi->param('serverpush'); + + return $serverpush; +} + +sub percentage { + my ($total, $count) = (@_); + return $total == 0 ? 0 : int($count*100/$total); +} + + +=head2 update + +Updates this test plan with new values supplied by the user. +Accepts a reference to a hash with keys identical to a test plan's +fields and values representing the new values entered. +Validation tests should be performed on the values +before calling this method. If a field is changed, a history +of that change is logged in the test_plan_activity table. + +=cut + + +sub log_activity { + my ($type, $obj_id, $field, $timestamp, $oldvalue, $newvalue) = @_; + my $dbh = Bugzilla->dbh; + $timestamp ||= get_time_stamp(); + + trick_taint($newvalue) if defined $newvalue; + my $field_id = Bugzilla::Testopia::Util::get_test_field_id($field, "test_". $type ."s"); + $dbh->do("INSERT INTO test_". $type ."_activity + (". $type . "_id, fieldid, who, changed, oldvalue, newvalue) + VALUES(?,?,?,?,?,?)", + undef, ($obj_id, $field_id, Bugzilla->user->id, + $timestamp, $oldvalue, $newvalue)); + +} + +sub process_list { + my ($ids) = @_; + + my @ids; + if (ref $ids eq 'ARRAY'){ + @ids = @$ids; + } + elsif ($ids =~ /,/){ + @ids = split(/[\s,]+/, $ids); + } + else { + push @ids, $ids if $ids; + } + + return @ids; +} + +=head1 AUTHOR + +Greg Hendricks + +=cut + +1; diff --git a/Bugzilla/Testopia/Xml.pm b/Bugzilla/Testopia/Xml.pm new file mode 100644 index 0000000..6f1de51 --- /dev/null +++ b/Bugzilla/Testopia/Xml.pm @@ -0,0 +1,678 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): David Koenig +# Greg Hendricks +# Jeff Dayley + +=head1 NAME + +Bugzilla::Testopia::Xml - Testopia Xml object + +=head1 DESCRIPTION + +This module parsers a XML representation of a Testopia Test Plans, +Test Cases, or Categories and stores them in Testopia if not errors +are detected. + +=head1 SYNOPSIS + +use Bugzilla::Testopia::Xml; + +=cut + +package Bugzilla::Testopia::Xml; +#use fields qw(testplans testcases tags categories builds); + +use strict; +#use base qw(Exporter); + +use Bugzilla::Config; +use Bugzilla::Product; +use Bugzilla::Testopia::Attachment; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::XmlTestCase; +use Bugzilla::User; +use Bugzilla::Util; + +############################### +#### Initialization #### +############################### + +=head1 CONSTANTS + +=over 4 + +=item STRIP_NONE - Do not strip white space from string. + +=item STRIP_LEFT - Strip white space from left side of string. + +=item STRIP_RIGHT - Strip white space from right side of string. + +=item STRIP_BOTH - Strinp white space from left and right side of string. + +=back + +=cut + +use constant AUTOMATIC => "AUTOMATIC"; +use constant BLOCKS => "blocks"; +our $DATABASE_DESCRIPTION = "Database_description"; +our $DATABASE_ID = "Database_id"; +use constant IGNORECASE => 1; +use constant DEPENDSON => "dependson"; +use constant PCDATA => "#PCDATA"; +use constant REQUIRE => "REQUIRE"; +use constant STRIP_NONE => 0; +use constant STRIP_LEFT => 1; +use constant STRIP_RIGHT => 2; +use constant STRIP_BOTH => 3; +our $TESTOPIA_GT = "&testopia_gt;"; +our $TESTOPIA_LT = "&testopia_lt;"; +use constant TESTPLAN_REFERENCE => "testplan_reference"; +our $XML_DESCRIPTION = "Xml_description"; +our $XML_AMP = "&[Aa][Mm][Pp];"; +our $XML_APOS = "&[Aa][Pp][Oo][Ss];"; +our $XML_GT = "&[Gg][Tt];"; +our $XML_LT = "&[Ll][Tt];"; +our $XML_QUOT = "&[Qq][Uu][Oo][Tt];"; + +use constant XMLREFERENCES_FIELDS => "Database_description Database_id Xml_description"; +@Bugzilla::Testopia::Xml::EXPORT = qw($DATABASE_DESCRIPTION $DATABASE_ID $XML_DESCRIPTION); + +use Class::Struct; +# +# The Xml structure is used to keep track of all new Testopia objects being created. +# +struct +( + 'Bugzilla::Testopia::Xml', + { + # Array of attachments read for xml source. + attachments => '@', +#TODO builds => '@', + # Array of categories read from xml source. Categories + categories => '@', + tags => '@', +#TODO testenvironments => '@', + # Array of testcases read from xml source. + testcases => '@', + # Hash of testcase aliases indexed by testcase summary. Used during verfication to + # insure that alias does not exist and that new aliases are used in only one testcase. + testcase_aliases => '%', + # Array of testplans read from xml source. + testplans => '@', + # If true indicates some type of error has occurred processing the XML. Used to prevent + # updating Testopia with contents of current XML. + parse_error => '$', + } +); + +#TODO: Add this to checksetup +use Text::Diff; + + +############################### +#### Methods #### +############################### + +=head1 METHODS + +=over 4 + +=item C + + Description: This method is called for any field that is stored in HTML format. The Testopia + entities are provided to allow users to pass HTML tags. For example, if you want to + store a < in Bold font, the following XML + &testopia_lt;B&testopia_gt;<&testopia_lt;/B&testopia_gt; + is passed to the HTML field as + < + Params: $string - String to convert. + Returns: converted string. + +=cut + +sub entity_replace_testopia +{ + my ($string) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/$TESTOPIA_GT/>/g; + $string =~ s/$TESTOPIA_LT//g; + + return $string; +} + +=pod + +=item C + + Description: This method is called for any field that is not stored in HTML format. The source + is XML so any XML entity will be in the &; format. These entities need to be + converted back to their character representation. + Params: $string - String to convert. + $strip - STRIP_NONE, STRIP_LEFT, STRIP_RIGHT, or STRIP_BOTH. White space stripping + is included because MySQL 5.0.3 retains trailing spaces when values are + stored and retrieved while prior versions stripped the trailing spaces. + Any non HTML field should use STRIP_BOTH to prevent searching issues if the + database was orginally pre MySQL 5.0.3. + Returns: converted string. + +=back + +=cut + +sub entity_replace_xml +{ + my ($string,$strip) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/^\s+// if ( $strip & STRIP_LEFT ); + $string =~ s/\s+$// if ( $strip & STRIP_RIGHT ); + $string =~ s/$XML_GT/>/g; + $string =~ s/$XML_LT/parse_error("TRUE"); +} + +sub parse() +{ + my ($self, $xml, $filename) = @_; + my @case_ids; + my $twig = XML::Twig->new( load_DTD => 1, keep_encoding => 1 ); + + if ( defined($xml) ) + { + $twig->parse($xml); + } + elsif ( defined($filename) ) + { + $twig->parsefile($filename); + } + else + { + $self->error("Bugzilla::Testopia::Xml::parse has no XML input source") + } + + my $root = $twig->root; + + # Check for unimplemented tags. + my @twig_builds = $root->children('build'); + $self->error("Support for tags has not been implemented.") if ( $#twig_builds != -1 ); + my @twig_testenvironments = $root->children('testenvironment'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testenvironments != -1 ); + my @twig_testruns = $root->children('testrun'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testruns != -1 ); + my @twig_testrunlogs = $root->children('testrunlog'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testrunlogs != -1 ); + + foreach my $twig_category ($root->children('category')) + { + my $category_name = entity_replace_xml($twig_category->field('name'),STRIP_BOTH); + my $product_name = entity_replace_xml($twig_category->att('product'),STRIP_BOTH); + my $description = entity_replace_xml($twig_category->field('description'),STRIP_BOTH); + if ( $category_name eq "" ) + { + $self->error("Category name cannot be empty, product='" . $product_name . "', description='" . $description . "'."); + next; + } + + $description = "FIX ME. Created during category import with no description supplied." if ( $description eq "" ); + + if ( $product_name eq REQUIRE ) + { + $self->error("Must supply a product for category '" . $category_name . "'." ); + next; + } + + my $product = new Bugzilla::Product({name => $product_name}); + if ( ! $product ) + { + $self->error("Cannot find product '" . $product_name . "' for category '" . $category_name . "'."); + $self->{"parser_error"} = 1; + next; + } + + my $category = new Bugzilla::Testopia::Category + ({ + name => $category_name, + product_id => $product->id(), + description => $description, + }); + + # Only create the category if it does not exist. + push @{$self->categories}, $category if ( ! $category->check_name($category_name) ); + } + + my $testplan = Bugzilla::Testopia::TestPlan->new({}); + my %plantype_ids; + my @temparray = @{$testplan->get_plan_types()}; + foreach my $arrayelement (@temparray) + { + my %temphash = %{$arrayelement}; + $plantype_ids{$temphash{"name"}} = $temphash{"id"}; + } + + foreach my $twig_testplan ($root->children('testplan')) + { + my $author = $twig_testplan->att('author'); + # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed + # are 'id' and 'login'. + my $author_ref = Bugzilla::User::match($author, 1, 0); + my $author_id = -1; + if ( ! $author_ref->[0] ) + { + $self->error("Cannot find author '" . $author . "' in test plan '" . $twig_testplan->field('name') . "'."); + } + else + { + my $author_user = $author_ref->[0]; + bless($author_user,"Bugzilla::User"); + $author_id = $author_user->id(); + } + + my $product_id = Bugzilla::Testopia::TestPlan::lookup_product_by_name($twig_testplan->field('product')); + if ( ! defined($product_id) ) + { + $self->error("Cannot find product '" . $twig_testplan->field('product') . "' in test plan '" . $twig_testplan->field('name') . "'."); + } + + my $name = entity_replace_xml($twig_testplan->field('name'),STRIP_BOTH) || undef; + $self->error("Found empty Test Plan name.") if ( ! defined($name) ); + $self->error("Length of Test Plan name '" . $name . "' must be " . Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH . " characters or less.") if ( defined($name) && ( length($name) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) ); + + $testplan = Bugzilla::Testopia::TestPlan->create({ + 'name' => $name, + 'product_id' => $product_id, + 'default_product_version' => entity_replace_xml($twig_testplan->field('productversion'),STRIP_BOTH), + 'type_id' => $plantype_ids{$twig_testplan->att('type')}, + 'text' => entity_replace_testopia($twig_testplan->field('document')), + 'author_id' => $author_id, + 'isactive' => entity_replace_xml($twig_testplan->att('archived'),STRIP_BOTH) =~/(true|1)/i ? 1 : 0, + 'creation_date' => entity_replace_xml($twig_testplan->field('created'),STRIP_BOTH) + }); + push @{$self->testplans}, $testplan; + + my @tags = $twig_testplan->children('tag'); + foreach my $twig_tag (@tags) + { + push @{$self->tags}, entity_replace_xml($twig_tag->text(),STRIP_BOTH); + } + + my @attachments = $twig_testplan->children('attachment'); + foreach my $twig_attachments (@attachments) + { + my $submitter = $twig_attachments->field('submitter'); + # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed + # are 'id' and 'login'. + my $submitter_ref = Bugzilla::User::match($submitter, 1, 0); + my $submitter_id = -1; + if ( ! $submitter_ref->[0] ) + { + $self->error("Cannot find submitter '" . $submitter . "' in test plan '" . $twig_testplan->field('name') . "' attachment '" . $twig_attachments->field('description') . "'."); + } + else + { + my $submitter_user = $submitter_ref->[0]; + bless($submitter_user,"Bugzilla::User"); + $submitter_id = $submitter_user->id(); + } + my $attachment = Bugzilla::Testopia::Attachment->new({ + 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), + 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), + 'submitter_id' => $submitter_id, + 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), + 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH) + }); + push @{$self->attachments}, $attachment; + } + } + + my $testcase = Bugzilla::Testopia::TestCase->new({ 'name' => 'dummy' }); + my %priority_ids; + @temparray = @{$testcase->get_priority_list()}; + foreach my $arrayelement (@temparray) + { + my %temphash = %{$arrayelement}; + my $longname = $temphash{"name"}; + # The long name. "P1 - Urgent" + $priority_ids{$longname} = $temphash{"id"}; + # The short name. "P1" + my $shortname = $longname; + $shortname =~ s/ - .*//; + $priority_ids{$shortname} = $temphash{"id"} if ( $longname ne $shortname ); + } + foreach my $twig_testcase ($root->children('testcase')) + { + my $summary = entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH) || undef; + $self->error("Found empty Test Case summary.") if ( ! defined($summary) ); + $self->error("Length of summary '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH . " characters or less.") if ( defined($summary) && ( length($summary) > Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH ) ); + my $author = $twig_testcase->att('author'); + # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed + # are 'id' and 'login'. + my $author_ref = Bugzilla::User::match($author, 1, 0); + my $author_id = -1; + if ( ! $author_ref->[0] ) + { + $self->error("Cannot find author '" . $author . "' in test case '" . $summary . "'."); + } + else + { + my $author_user = $author_ref->[0]; + bless($author_user,"Bugzilla::User"); + $author_id = $author_user->id(); + } + my $tester = entity_replace_xml($twig_testcase->field('defaulttester'),STRIP_BOTH); + # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed + # are 'id' and 'login'. + my $tester_ref = Bugzilla::User::match($tester, 1, 0); + my $tester_id = -1; + if ( ! $tester_ref->[0] ) + { + $self->error("Cannot find default tester '" . $tester . "' in test case '" . $summary . "'."); + } + else + { + my $tester_user = $tester_ref->[0]; + bless($tester_user,"Bugzilla::User"); + $tester_id = $tester_user->id(); + } + my $status_id = Bugzilla::Testopia::TestCase::lookup_status_by_name($twig_testcase->att('status')); + $self->error("Cannot find status '" . $twig_testcase->att('status') . "' in test case '" . $summary . "'.") if ( ! defined($status_id) ); + + my $xml_testcase = new Bugzilla::Testopia::XmlTestCase; + $xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + push @{$self->testcases}, $xml_testcase; + my $alias = entity_replace_xml($twig_testcase->field('alias'),STRIP_BOTH) || undef; + $self->error("Length of alias '" . $alias . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH . " characters or less.") if ( defined($alias) && ( length($alias) > Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH ) ); + my $requirement = entity_replace_xml($twig_testcase->field('requirement'),STRIP_BOTH) || undef; + $self->error("Length of requirement '" . $requirement . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less.") if ( defined($requirement) && ( length($requirement) > Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH ) ); + + $xml_testcase->testcase(Bugzilla::Testopia::TestCase->new({ + 'action' => entity_replace_testopia($twig_testcase->field('action')), + 'alias' => $alias, + 'arguments' => entity_replace_xml($twig_testcase->field('arguments'),STRIP_NONE), + 'author_id' => $author_id, + 'blocks' => undef, + 'breakdown' => entity_replace_testopia($twig_testcase->field('breakdown')), + 'case_status_id' => $status_id, + 'category_id' => undef, + 'default_tester_id' => $tester_id, + 'dependson' => undef, + 'effect' => entity_replace_testopia($twig_testcase->field('expectedresults')), + 'isautomated' => ( uc $twig_testcase->att('automated') ) eq AUTOMATIC ? 1 : 0, + 'plans' => undef, + 'priority_id' => $priority_ids{$twig_testcase->att('priority')}, + 'requirement' => $requirement, + 'setup' => entity_replace_testopia($twig_testcase->field('setup')), + 'script' => entity_replace_xml($twig_testcase->field('script'),STRIP_NONE), + 'summary' => $summary, + })); + + foreach my $twig_testplan_reference ( $twig_testcase->children(TESTPLAN_REFERENCE) ) + { + my $testplan_reference = $twig_testplan_reference->children_text(PCDATA); + if ( $testplan_reference eq "" ) + { + $self->error("No test plan included for type '" + . $twig_testplan_reference->att('type') + . "' in test case '" . $twig_testcase->field('summary') . "'." ); + } + elsif ( length($testplan_reference) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) + { + $self->error("Length of Test Plan name '" . $testplan_reference . "' for test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less."); + } + elsif ( ! $xml_testcase->testplan->add($twig_testplan_reference->att('type'),entity_replace_xml($testplan_reference,STRIP_BOTH)) ) + { + $self->error("Do not know how to handle test plan of type '" + . $twig_testplan_reference->att('type') + . "' in test case '" . $twig_testcase->field('summary') . "'." + . "\nKnown types are: (" . uc XMLREFERENCES_FIELDS . ")."); + } + } + # Keep track of this testcase's alias. Used during verification to insure aliases are unique. + $self->testcase_aliases(entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH),$alias) if ( defined $alias ); + # Keep track of this testcase's category. To create a category at this time would require + # getting the product from the Test Plan that this Test Case is associated with. The category + # will created when each Test Case is stored. + my $categoryname = entity_replace_xml($twig_testcase->field('categoryname'),STRIP_BOTH); + if ( $categoryname ne "" ) + { + $xml_testcase->category($categoryname); + } + else + { + $self->error("Empty category name for test case '" . $summary . "'."); + } + + my @attachments = $twig_testcase->children('attachment'); + foreach my $twig_attachments (@attachments) + { + my $submitter = $twig_attachments->field('submitter'); + # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed + # are 'id' and 'login'. + my $submitter_ref = Bugzilla::User::match($submitter, 1, 0); + my $submitter_id = -1; + if ( ! $submitter_ref->[0] ) + { + $self->error("Cannot find submitter '" . $submitter . "' in test case '" . $twig_testcase->field('summary') . "' attachment '" . $twig_attachments->field('description') . "'."); + } + else + { + my $submitter_user = $submitter_ref->[0]; + bless($submitter_user,"Bugzilla::User"); + $submitter_id = $submitter_user->id(); + } + my $attachment = { + 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), + 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), + 'submitter_id' => $submitter_id, + 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), + 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH) + }; + $xml_testcase->add_attachment($attachment); + } + + my @tags = $twig_testcase->children('tag'); + foreach my $twig_tag ( @tags ) + { + my $tag = entity_replace_xml($twig_tag->text(),STRIP_BOTH); + $self->error("Length of tag '" . $tag . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH . " characters or less.") if ( defined($tag) && ( length($tag) > Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH ) ); + $xml_testcase->add_tag($tag); + } + + my @components = $twig_testcase->children('component'); + foreach my $twig_component ( @components ) + { + my $results = $xml_testcase->add_component(entity_replace_xml($twig_component->children_text(PCDATA),STRIP_BOTH),$twig_component->att('product')); + $self->error($results) if ( $results ne "" ); + } + foreach my $twig_blocks ( $twig_testcase->children(BLOCKS) ) + { + if ( ! $xml_testcase->blocks->add($twig_blocks->att('type'),entity_replace_xml($twig_blocks->children_text(PCDATA),STRIP_BOTH)) ) + { + $self->error("Do not know how to handle a blocking test case of type '" . $twig_blocks->att('type') . "' in test case '" . $xml_testcase->testcase->summary() . "'.") + } + } + + foreach my $twig_dependson ( $twig_testcase->children(DEPENDSON) ) + { + if ( ! $xml_testcase->dependson->add($twig_dependson->att('type'),entity_replace_xml($twig_dependson->children_text(PCDATA),STRIP_BOTH)) ) + { + $self->error("Do not know how to handle dependency of type '" . $twig_dependson->att('type') . "' in test case '" . entity_replace_xml($xml_testcase->testcase->summary(),STRIP_BOTH) . "'.") + } + } + } + + # + # Start of data integrity check. + # + # Run through the Test Plans and Test Cases looking for integrity errors. + # + + # Check for duplicate aliases. Loop though all testcases that have a alias. + my %used_alias; + my %duplicate_alias; + foreach my $summary ( keys %{$self->testcase_aliases} ) + { + # Get the alias. + my $alias = $self->testcase_aliases($summary); + # Is the alias used by a testcase in the database already? If so add it to the duplicate list + # and move onto next testcase. + my $alias_testcase_id = Bugzilla::Testopia::TestCase::class_check_alias($alias); + if ( $alias_testcase_id ) + { + $duplicate_alias{$alias} = $alias_testcase_id; + next; + } + # Is the alias in the used_alias array? + if ( defined( $used_alias{$alias} ) ) + { + # If so then another testcase being created also used the alias. Add the alias to the + # duplicate list. + $duplicate_alias{$alias} = "import"; + } + else + { + # Alias has not been seen yet. Add it to the used_alias list to keep track of it. + $used_alias{$alias} = ""; + } + } + # The @duplicate_alias list contains aliases used by more that one test case. Display them and set + # error condition + foreach my $summary ( keys %{$self->testcase_aliases} ) + { + my $alias = $self->testcase_aliases($summary); + if ( exists $duplicate_alias{$alias} ) + { + my $error_message = "Test Case '" . $summary . "' has a non-unique alias '" . $alias . "'."; + if ( $duplicate_alias{$alias} ne "import" ) + { + $error_message .= " Test Case " . $duplicate_alias{$alias} . " already uses the alias '" . $alias . "'."; + } + else + { + $error_message .= " Additional test cases being imported are using the alias '" . $alias . "'."; + } + $self->error($error_message); + } + } + + # + # Start of data store. + # + # No data has been written prior to this point. If parse_error has not been set the XML is valid + # and no integrity errors were found. It's time to start storing the Test Plans and Test Cases. + # + + if ( ! defined $self->parse_error ) + { + # Store new categories. + foreach my $category ( @{$self->categories} ) + { + # Make sure category still does not exist. We don't check for uniqueness above so + # the same category could be defined multiple times. + if ( ! $category->check_name($category->name()) ) + { + $category->store(); + my $product_name = Bugzilla::Testopia::Product->new($category->product_id())->name(); + print "Created category '" . $category->name() . "': " . $category->description() . " for product " . $product_name . ".\n"; + } + } + + # Store new testplans. + foreach my $testplan ( @{$self->testplans} ) + { + foreach my $asciitag ( @{$self->tags} ) + { + my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag}); + my $tagid = $classtag->store; + $testplan->{'tag_id'} = $tagid; + $testplan->add_tag($tagid); + } + foreach my $attachment ( @{$self->attachments} ) + { + $attachment->{'plan_id'} = $testplan->id; + Bugzilla::Testopia::Attachment->create($attachment); + } + print "Created Test Plan ". $testplan->id . ": " . $testplan->name() . "\n"; + } + + # Store new testcases. + foreach my $testcase ( @{$self->testcases} ) + { + bless($testcase,"Bugzilla::Testopia::XmlTestCase"); + my $result = $testcase->store(@{$self->testplans}); + if ( $result ne "" ) + { + $self->error($result); + } + else + { + push @case_ids, $testcase->testcase->id(); + print "Created Test Case " . $testcase->testcase->id() . ": " . $testcase->testcase->summary() . "\n" if $filename; + } + } + + # Now that each testcase has been stored we loop though them again and create + # relationships like blocks or dependson. + foreach my $testcase ( @{$self->testcases} ) + { + $testcase->store_relationships(@{$self->testcases}); + } + } + + $twig->purge; + return \@case_ids; +} + +=head1 SEE ALSO + +Testopia::(TestPlan, TestCase, TestRun, Category, Build, Environment) + +=head1 AUTHOR + +David Koenig + +=cut + +1; diff --git a/Bugzilla/Testopia/XmlReferences.pm b/Bugzilla/Testopia/XmlReferences.pm new file mode 100644 index 0000000..d7e48cd --- /dev/null +++ b/Bugzilla/Testopia/XmlReferences.pm @@ -0,0 +1,144 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): David Koenig + +package Bugzilla::Testopia::XmlReferences; + +use constant IGNORECASE => "ignorecase"; + +use strict; + +sub new { + my ($invocant,$ignorecase,$fields) = @_; + + my $class = ref($invocant) || $invocant; + my $self = {}; + bless($self, $class); + $self->{IGNORECASE} = $ignorecase; + for my $field ( split(/ /, $fields) ) { + $field = uc $field if ( $self->{IGNORECASE} ); + $self->{$field} = []; + } + return $self; +} + +sub add { + my ($self, $type, $object) = @_; + + $type = uc $type if ( $self->{IGNORECASE} ); + + return 0 if ( ! exists $self->{$type} ); + + push @{$self->{$type}}, $object; +} + +sub display { + my ($self) = @_; + + print "display() self=" . $self . "\n"; + foreach my $key (keys %$self) { + if ( defined $self->{$key} ) { + print "display() key=$key value=" . $self->{$key} . "\n"; + } + else { + print "display() key=$key value=undefined\n"; + } + } +} + +sub get { + my ($self, $type) = @_; + + $type = uc $type if ( $self->{IGNORECASE} ); + + return 0 if ( ! exists $self->{$type} ); + + return $self->{$type}; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::XmlReferences - Testopia XmlReferences object + +=head1 DESCRIPTION + +This module maintains references to objects while the XML data is +being imported. Test plans and Test cases can be referenced by +database id, database description or XML description. The references +are stored here and processed as needed. + +=head1 SYNOPSIS + + use Bugzilla::Testopia::XmlReferences; + + $xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + +=head1 METHODS + +=over + +=item C + + Description: Creates a new reference. + + Params: Constants - IGNORECASE + + Returns: Blessed XmlReferences object. + +=item C + + Description: Add a new object reeference of the supplied type. + + Params: type - dependson, blocks, plan + object - case or plan + + Returns: Nothing. + +=item C + + Description: displays the cureent references on the screen. + + Params: none. + + Returns: Nothing. + +=item C + + Description: Returns the references of the specified type. + + Params: type + + Returns: references. + +=back + + +=head1 SEE ALSO + +Testopia::(TestPlan, TestCase, XmlTestCase, Xml) + +=head1 AUTHOR + +David Koenig diff --git a/Bugzilla/Testopia/XmlTestCase.pm b/Bugzilla/Testopia/XmlTestCase.pm new file mode 100644 index 0000000..4c769e5 --- /dev/null +++ b/Bugzilla/Testopia/XmlTestCase.pm @@ -0,0 +1,485 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): David Koenig +# Jeff Dayley +# Greg Hendricks + +package Bugzilla::Testopia::XmlTestCase; +#use fields qw(testplans testcases tags categories builds); + +use strict; + +use Bugzilla::Product; +use Bugzilla::Testopia::Attachment; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::XmlReferences; +use Bugzilla::Testopia::Product; +use Bugzilla::User; +use Bugzilla::Util; + +use Class::Struct; + +struct( + 'Bugzilla::Testopia::XmlTestCase', + { + attachments => '@', + blocks => 'Bugzilla::Testopia::XmlReferences', + category => '$', + component_ids => '@', + dependson => 'Bugzilla::Testopia::XmlReferences', + tags => '@', + testcase => '$', + testplan => 'Bugzilla::Testopia::XmlReferences', + } +); + +sub add_attachment(){ + my ($self,$attachment) = @_; + + push @{$self->attachments}, $attachment; +} + +sub add_tag(){ + my ($self,$tag) = @_; + + push @{$self->tags}, $tag; +} + +sub get_available_products { + my $dbh = Bugzilla->dbh; + + my $products = $dbh->selectall_arrayref( + "SELECT id, name + FROM products + ORDER BY name", + {"Slice"=>{}}); + return $products; +} + +sub add_component { + my ($self,$component,$component_product) = @_; + my $component_id = ""; + my $product_id = ""; + + return "Component $component needs to provide a product." if ( $component_product eq "" ); + + # Find the product identifier. + my $products_ref = get_available_products(); + foreach my $product (@$products_ref){ + if ( $component_product eq $product->{name} ){ + $product_id = $product->{id}; + last; + } + } + return "Cannot find product $component_product for component $component." if ( $product_id eq "" ); + + # Find the component identifier for the product's componet + my $product = Bugzilla::Testopia::Product->new($product_id); + my $components_ref = $product->components; + foreach my $product_component ( @$components_ref ) { + if ( $component eq $product_component->name ) { + $component_id = $product_component->id; + last; + } + } + return "Product $component_product does not have a component named $component." if ( $component_id eq "" ); + + # Save the component identifier for this Test Case. + push @{$self->component_ids}, $component_id; + + return ""; +} + +sub debug_display { + my ($self) = @_; + my $display_variable = ""; + + my %text = %{$self->testcase->text()} if ( defined $self->testcase->text() ); + print STDERR "Testcase: " . $self->testcase->summary() . "\n"; + my $testcase_id = "null"; + $testcase_id = $self->testcase->id() if ( $self->testcase->id() ); + print STDERR " ID " . $testcase_id . "\n"; + print STDERR " Action\n"; + if ( defined $text{'action'} ){ + my @results = split(/\n/,$text{'action'}); + foreach my $result (@results){ + print STDERR " $result\n"; + } + } + my $alias = "null"; + $alias = $self->testcase-alias() if ( $self->testcase->alias() ); + print STDERR " Alias " . $alias . "\n"; + my %author = %{$self->testcase->author()}; + my $author_id = $author{"id"}; + my $author_login = $author{"login"}; + print STDERR " Author " . $author_login . " (id=" . $author_id . ", hash=" . $self->testcase->author() . ")\n"; + print STDERR " Blocks " . $self->blocks->display() . "\n"; + print STDERR " Category " . $self->category . "\n"; + print STDERR " Depends On " . $self->dependson->display() . "\n"; + print STDERR " Expected Results\n"; + if ( defined $text{'effect'} ){ + my @results = split(/\n/,$text{'effect'}); + foreach my $result (@results){ + print STDERR " $result\n"; + } + } + print STDERR " Summary " . $self->testcase->summary() . "\n"; + #TODO:print STDERR " Default Product Version " . $self->testcase->product_version() . "\n"; + #TODO:print STDERR " Document " . $self->testcase->text() . "\n"; + my %tester = %{$self->testcase->default_tester()}; + my $tester_id = $tester{"id"}; + my $tester_login = $tester{"login"}; + print STDERR " Tester " . $tester_login . " (id=" . $tester_id . ", hash=" . $self->testcase->default_tester() . ")\n"; + print STDERR " Is Automated " . $self->testcase->isautomated() . "\n"; + #TODO:print STDERR " Plans " . $self->testcase->plans() . "\n"; + #TODO:print STDERR " Priority " . $self->testcase->priority_id() . "\n"; + #TODO:print STDERR " Product " . $self->testcase->product_id() . "\n"; + print STDERR " Requirement " . $self->testcase->requirement() . "\n"; + + print STDERR " Script " . $self->testcase->script() . "\n"; + print STDERR " Script Arguments " . $self->testcase->arguments() . "\n"; + print STDERR " Status " . $self->testcase->status() . "\n"; + + foreach my $tag (@{$self->tags}){ + print STDERR " Tag " . $tag . "\n"; + } + + my @attachments = @{$self->testcase->attachments()}; + foreach my $attachment (@attachments) { + my %submitter = %{$self->testcase->submitter()}; + $author_login = $author{"login"}; + print STDERR " Attachment " . $attachment->description() . "\n"; + print STDERR " Filename " . $attachment->filename() . "\n"; + print STDERR " Mime Type " . $attachment->mime_type(). "\n"; + print STDERR " Submitter " . $author_login . "\n"; + } +} + +sub get_testcase_ids { + my ($self, $field, @new_testcases) = @_; + my $error_message = ""; + + my @testcase_id = @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)}; + + foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) { + foreach my $testcase (@new_testcases) { + push @testcase_id, $testcase->testcase->{'case_id'} if ( $testcase->testcase->{'summary'} eq $testcase_summary ); + } + } + + #TODO Testplans using Database_Description + foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) { + $error_message .= "\n" if ( $error_message ne "" ); + $error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup for blocking test case " . $testcase_summary . "' for Test Case '". $self->testcase->summary . "'."; + } + return $error_message if ( $error_message ne "" ); + + my @return_testcase_id; + foreach my $testcase_id (@testcase_id) { + my $testcase = Bugzilla::Testopia::TestCase->new($testcase_id); + if ( ! defined($testcase) ) { + $error_message .= "\n" if ( $error_message ne "" ); + $error_message .= "Could not find blocking Test Case '" . $testcase_id . "' for Test Case '". $self->testcase->summary . "'."; + } + else { + push @return_testcase_id, $testcase->id(); + } + } + return $error_message if ( $error_message ne "" ); + + return @return_testcase_id; +} + +sub store { + my ($self, @new_testplans) = @_; + my $error_message = ""; + my @testplan_id = @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)}; + + # If we have any references to test plans from the XML data we need to search the @new_testplans + # array to find the actual test plan id. The new_testplans array contains all test plans created + # from the XML. + # Order of looping does not matter, number of test plans associated to each test case should be small. + foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) { + foreach my $testplan (@new_testplans) { + push @testplan_id, $testplan->id() if ( $testplan->name() eq $testplan_name ); + } + } + + #TODO Testplans using Database_Description + foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) { + $error_message .= "\n" if ( $error_message ne "" ); + $error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup of test plan " . $testplan_name . "' for Test Case '". $self->testcase->{'summary'} . "'."; + } + return $error_message if ( $error_message ne "" ); + + # Have to have a testplan to determine valid categories for testcase. + return "Test Case '" . $self->testcase->{'summary'} . "' needs a Test Plan." if ( $#testplan_id == -1 ); + + # Verify that each testplan exists. + my @testplan; + foreach my $testplan_id (@testplan_id) { + my $testplan = Bugzilla::Testopia::TestPlan->new($testplan_id); + if ( ! defined($testplan) ) { + $error_message .= "\n" if ( $error_message ne "" ); + $error_message .= "Could not find Test Plan '" . $testplan_id . "' for Test Case '". $self->testcase->{'summary'} . "'."; + } + else { + push @testplan, $testplan; + } + } + return $error_message if ( $error_message ne "" ); + + # Verify that each testplan has the testcase's category associated to it. If the category does not + # exist it will be created. + foreach my $testplan (@testplan) { + my $category = $testplan->product->categories->[0]; + + my $categoryid = check_case_category($self->category, new Bugzilla::Testopia::Product($testplan->product_id)) if ( defined($category) ); + if ( ! defined($categoryid) ) { + my $new_category = Bugzilla::Testopia::Category->create({ + product_id => $testplan->product_id, + name => $self->category, + description => "FIX ME. Created during Test Plan import." + }); + $categoryid = $new_category->id; + } + $self->testcase->{'category_id'} = $categoryid if ( ! defined($self->testcase->{'category_id'}) ); + } + $self->testcase->{plans} = \@testplan; + my $case = Bugzilla::Testopia::TestCase->create($self->testcase); + $self->testcase->{'case_id'} = $case->id; + foreach my $attachment ( @{$self->attachments} ) { + $attachment->{'case_id'} = $case->id; + $attachment = Bugzilla::Testopia::Attachment->create($attachment); + } + foreach my $asciitag ( @{$self->tags} ) { + $case->add_tag($asciitag); + } + + # Link the testcase to each of it's testplans. + foreach my $testplan ( @testplan ) { + $case->link_plan($testplan->id(),$case->id) + } + + # Code below requires the testplans to be linked into testcases before being run. + $case->add_component($self->component_ids); + + return $error_message; +} + +sub store_relationships { + my ($self, @new_testcases) = @_; + return unless $self->testcase->{'case_id'}; + my $testcase = Bugzilla::Testopia::TestCase->new($self->testcase->{'case_id'}); + + # Hashes are used because the entires in blocks and dependson must be unique. + my %blocks = (); + foreach my $block ( $self->get_testcase_ids("blocks",@new_testcases) ) { + $blocks{$block}++; + } + + my $blocks_size = keys( %blocks ); + my %dependson = (); + foreach my $dependson ( $self->get_testcase_ids("dependson",@new_testcases) ) { + $dependson{$dependson}++; + } + + my $dependson_size = keys( %dependson ); + if ( ( $blocks_size > 0 ) || ( $dependson_size > 0 ) ) { + # Need to add the current blocks and dependson from the Test Case; otherwise, they will + # be removed. + foreach my $block ( split(/ /,$testcase->blocked_list_uncached()) ) { + $blocks{$block}++; + } + + foreach my $dependson ( split(/ /,$testcase->dependson_list_uncached()) ) { + $dependson{$dependson}++; + } + + my @blocks = keys(%blocks); + my @dependson = keys(%dependson); + $testcase->update_deps( join(' ',@dependson ),join(' ',@blocks) ); + } +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::XmlTestCase + +=head1 DESCRIPTION + +The XmlTestCase structure stores data for the verfication processing. The database is not updated +until a verfication pass is made through the XML data. Some of the TestCase class references are +database references that will not be valid until the class has been stored in the database. This +structure stores these references to be used during verfication and writting to the database. + +=head1 SYNOPSIS + + $testcase = Bugzilla::Testopia::XMLTestcase->new($case_hash_ref); + + $testcase->store(); + +=head1 FILEDS + +=over + +=item C + +Array - List of attachment hashes that will be used to create Testopia::Attachment objects +for this test case. + +=item C + +Testopia::XMLReferences Object representing the test cases that this tests case blocks. + +=item C + +Testopia::Category object representing the category this case will belong to. + +=item C + +List of Bugzilla::Component ids that will be attached to this test case. + +=item C + +Testopia::XMLReferences Object representing the test cases that this tests case depends on. + +=item C + +Array of strings - tags to apply to this test case. + +=item C + +Hash representation of the test case that will be created. + +=item C + +Bugzilla::Testopia::XmlReferences of plans to link to this test case + +=back + +=head1 METHODS + +=over + +=item C + + Description: Adds an attachment to the list for later inclusion + + Params: attachment - hash representing the Testopia::Attachment to add. + + Returns: Nothing. + +=item C + + Description: Adds a component to the list for later inclusion + + Params: component_id - id of component to add. + product_id - id of the product this component belongs to. + + Returns: Nothing. + +=item C + + Description: Adds a tag for later inclusion. + + Params: string - tag name + + Returns: Nothing. + +=item C + + Description: Prints debug information to STDERR + + Params: None. + + Returns: Nothing. + +=item C + + Description: Gets a list of the newly created testcases for use in setting up relationships + + Params: field - dependson or blocks. + cases - array of test cases newly created by store. + + Returns: A list of case ids. + +=item C + + Description: Saves a imported Test Case. This method insures that all Test Case attributes not stored + in the Test Case object are created. The attributes include the Test Plan, tags, compoents, + attachments and categories. + + Params: test_plans - list of plans to link the newly created cases to. + + Returns: error - if the store was not successful, an error message is returned. + +=item C + + Description: Save the dependson and blocks relationships between Test Cases. This method can only be + called after the Test Cases being imported have been stored. The dependson and blocks + relationships use the Test Case identifier which is created only after the Test Case has + been stored. + + Params: test_cases - array of newly created case objects. + + Returns: nothing. + +=back + +=head1 FUNCTIONS + +=over + +=item C + + Description: Returns a list of products. This is the same code as Bugzilla::Testopia::TestPlan->get_available_products + without view restrictions. + + Params: None. + + Returns: A array of product information. + +=back + +=head1 SEE ALSO + +=over + +L + +L + +L + +=back + +=head1 AUTHOR + +David Koenig diff --git a/Bugzilla/WebService/Testopia/Build.pm b/Bugzilla/WebService/Testopia/Build.pm new file mode 100644 index 0000000..9a3c6cb --- /dev/null +++ b/Bugzilla/WebService/Testopia/Build.pm @@ -0,0 +1,279 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::Build; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::Product; + +sub get { + my $self = shift; + my ($build_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a build object hash + my $build = new Bugzilla::Testopia::Build($build_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build; + ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit; + + $build->run_count(); + + return $build; +} + +sub check_build { + my $self = shift; + my ($name, $product) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + if ($product =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($product); + } + else { + $product = Bugzilla::Product::check_product($product); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + + return Bugzilla::Testopia::Build::check_build($name, $product, "THROWERROR"); +} + +sub create{ + my $self = shift; + my ($new_values) = @_; # Required: name, product_id + + Bugzilla->login(LOGIN_REQUIRED); + + $new_values->{'product_id'} ||= $new_values->{'product'}; + delete $new_values->{'product'}; + + my $product; + if ($new_values->{'product_id'} =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($new_values->{'product_id'}); + } + else { + $product = Bugzilla::Product::check_product($new_values->{'product_id'}); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + + $new_values->{'milestone'} ||= $product->default_milestone; + if (! defined $new_values->{'isactive'}){ + $new_values->{'isactive'} = 1; + } + + my $build = Bugzilla::Testopia::Build->create($new_values); + + # Result is new build + return $build; +} + +sub update{ + my $self = shift; + my ($id, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $build = new Bugzilla::Testopia::Build($id); + ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Build'}) unless $build; + ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit; + + $build->set_name($new_values->{'name'}) if $new_values->{'name'}; + $build->set_description($new_values->{'description'}) if defined $new_values->{'description'}; + $build->set_milestone($new_values->{'milestone'}) if $new_values->{'milestone'}; + $build->set_isactive($new_values->{'isactive'} =~ /(true|1|yes)/i ? 1 : 0) if defined $new_values->{'isactive'}; + + $build->update; + + return $build; +} + +# DEPRECATED use Build::get instead +sub lookup_name_by_id { + my $self = shift; + my ($build_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + die "Invalid Build ID" + unless defined $build_id && length($build_id) > 0 && $build_id > 0; + + my $build = new Bugzilla::Testopia::Build($build_id); + ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit; + + my $result = defined $build ? $build->name : ''; + + # Result is build name string or empty string if ERROR + return $result; +} + +# DEPRECATED use Build::check_build($name, $product) instead +sub lookup_id_by_name { + return { ERROR => 'This method is considered harmful and has been deprecated. Please use Build::check_build instead'}; +} + +sub get_runs { + my $self = shift; + my ($build_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $build = new Bugzilla::Testopia::Build($build_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build; + ThrowUserError('testopia-read-only', {'object' => $build}) unless $build->product->canview; + + # Result is list of test runs for the given build + return $build->runs(); +} + +sub get_caseruns { + my $self = shift; + my ($build_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $build = new Bugzilla::Testopia::Build($build_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build; + ThrowUserError('testopia-read-only', {'object' => $build}) unless $build->product->canview; + + # Result is list of test runs for the given build + return $build->caseruns(); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::Build + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia Builds + +=head1 METHODS + +=over + +=item C + + Description: Looks up and returns a build by name. + + Params: $name - String: name of the build. + $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Hash: Matching Build object hash or error if not found. + +=item C + + Description: Creates a new build object and stores it in the database + + Params: $values - Hash: A reference to a hash with keys and values + matching the fields of the build to be created. + +-------------+----------------+-----------+------------------------------------+ + | Field | Type | Null | Description | + +-------------+----------------+-----------+------------------------------------+ + | product | Integer/String | Required | ID or Name of product | + | name | String | Required | | + | milestone | String | Optional | Defaults to product's default MS | + | description | String | Optional | | + | isactive | Boolean | Optional | Defaults to True (1) | + +-------------+----------------+-----------+------------------------------------+ + + Returns: The newly created object hash. + +=item C + + Description: Used to load an existing build from the database. + + Params: $id - An integer representing the ID in the database + + Returns: A blessed Bugzilla::Testopia::Build object hash + +=item C + + Description: Returns the list of case-runs that this Build is used in. + + Params: $id - Integer: Build ID. + + Returns: Array: List of case-run object hashes. + +=item C + + Description: Returns the list of runs that this Build is used in. + + Params: $id - Integer: Build ID. + + Returns: Array: List of run object hashes. + +=item C B Use Build::check_build instead + +=item C B Use Build::get instead + +=item C + + Description: Updates the fields of the selected build or builds. + + Params: $id - Integer: A single build ID. + + $values - Hash of keys matching Build fields and the new values + to set each field to. + +-------------+----------------+ + | Field | Type | + +-------------+----------------+ + | name | String | + | milestone | String | + | description | String | + | isactive | Boolean | + +-------------+----------------+ + + Returns: Hash: The updated Build object hash. + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/Environment.pm b/Bugzilla/WebService/Testopia/Environment.pm new file mode 100644 index 0000000..1c75e72 --- /dev/null +++ b/Bugzilla/WebService/Testopia/Environment.pm @@ -0,0 +1,386 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::Environment; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +sub get { + my $self = shift; + my ($environment_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $environment = new Bugzilla::Testopia::Environment($environment_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment; + ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview; + + #Result is a environment hash map + return $environment; +} + +sub check_environment { + my $self = shift; + my ($name, $product) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + if ($product =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($product); + } + else { + $product = Bugzilla::Product::check_product($product); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + + return Bugzilla::Testopia::Environment::check_environment($name, $product, 'THROWERROR'); +} + +sub list { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + $cgi->param("current_tab", "environment"); + foreach (keys(%$query)){ + $cgi->param($_, $query->{$_}); + } + + my $search = Bugzilla::Testopia::Search->new($cgi); + + # Result is an array of environment hash maps + return Bugzilla::Testopia::Table->new('environment', 'tr_xmlrpc.cgi',$cgi,undef, $search->query())->list(); + +} + +sub create { + my $self = shift; + my ($new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Setup field name aliasing + $new_values->{'product_id'} ||= $new_values->{'product'}; + delete $new_values->{'product'}; + + my $product; + if ($new_values->{'product_id'} =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($new_values->{'product_id'}); + } + else { + $product = Bugzilla::Product::check_product($new_values->{'product_id'}); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + + if (! defined $new_values->{'isactive'}){ + $new_values->{'isactive'} = 1; + } + + my $environment = Bugzilla::Testopia::Environment->create($new_values); + + # Result is new environment + return $environment; +} + +sub create_full { + my $self = shift; + my ($env_basename, $product, $environment) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + if ($product =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($product); + } + else { + $product = Bugzilla::Product::check_product($product); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + + my $env_id = Bugzilla::Testopia::Environment->create_full($env_basename, $product->id, $environment); + + return $env_id; +} + +sub update { + my $self = shift; + my ($environment_id, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + my $environment = new Bugzilla::Testopia::Environment($environment_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment; + ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canedit; + + $environment->set_name($new_values->{'name'}) if $new_values->{'name'}; + $environment->set_isactive($new_values->{'isactive'} =~ /(true|1|yes)/i ? 1 : 0) if defined $new_values->{'isactive'}; + + $environment->update(); + + # Result is modified environment, otherwise an exception will be thrown + return $environment; +} + +sub get_runs { + my $self = shift; + my ($environment_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $environment = new Bugzilla::Testopia::Environment($environment_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment; + ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview; + + # Result is list of test runs for the given environment + return $environment->runs(); +} + +sub get_caseruns { + my $self = shift; + my ($environment_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $environment = new Bugzilla::Testopia::Environment($environment_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment; + ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview; + + # Result is list of test runs for the given environment + return $environment->caseruns(); +} + +1; +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::Environment + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia Environments + +=head1 METHODS + +=over + +=item C + + Description: Looks up and returns an environment by name. + + Params: $name - String: name of the environment. + $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Hash: Matching Environment object hash or error if not found. + +=item C + + Description: Creates a new environment object and stores it in the database + + Params: $values - Hash: A reference to a hash with keys and values + matching the fields of the environment to be created. + +-------------+----------------+-----------+------------------------------------+ + | Field | Type | Null | Description | + +-------------+----------------+-----------+------------------------------------+ + | product_id | Integer/String | Required | ID or Name of product | + | name | String | Required | | + | isactive | Boolean | Optional | Defaults to True (1) | + +-------------+----------------+-----------+------------------------------------+ + + Returns: The newly created object hash. + +=item C + + Description: When an environment starting with $basename does not exist yet + exactly matching $envhash, creates a new environment object, and any new + elements, properties, values, and the mapping between them and stores it in + the database. The full environment name that is created will be a conjunction + of the $basename and a date and time stamp. Else returns id of existing env. + + Params: $basename - String: starting name of the environment (remainder + will be a date time conjunction added by this function) + $product - Integer/String: product name or id of the product in the Database + $envhash - Hash ref: Multilevel hash following a format: Top level hash + keys are assumed to be categories. Bottom level + values (leaves) and their immediate keys are assumed + to be the values and properties in the database, + respectively. Between the top level and bottom level + keys are the elements. Everything will be created + if it does not yet exist in the database except for + the categories which must exist beforehand. + +Here is an example of an $envhash (quotes removed): + +{ + +Hardware => { + + System Board => { + Type => i386, + Manufacturer => Ssystem manufacturer, + Model => System product name + }, + + Memory => { + Total Physical => 2,015.33 MB + } + + }, + +Software => { + + BIOS => { + Version/Date => American Megatrends Inc. 1.00, 11/25/2003 + }, + + Operating System => { + + Version => 5.2.3790 Service Pack 2 Build 3790, + Manufacturer => Microsoft Corporation, + Name => Microsoft(R) Windows(R) Server 2003, Standard Edition + } + + }, + +Harddrives => { + + 1 => { + Card => 0, + Firmware Revision => 34.06J34, + Channel => 6, + Model => WDC WD360GD-00FNA0 + }, + + 0 => { + Card => 0, + Firmware Revision => 35.06K35, + Channel => 7, + Model => WDC WD360GD-00FNA0 + }, + + 2 => { + Card => 0, + Firmware Revision => 27.08D27, + Channel => 5, + Model => WDC WD740GD-00FLA1 + }, + + }, + +}; + + + Returns: The environment id of the newly created or matching environment. + +=item C + + Description: Used to load an existing Environment from the database. + + Params: $id - An integer representing the ID in the database + + Returns: A blessed Bugzilla::Testopia::Environment object hash + +=item C + + Description: Returns the list of case-runs that this Environment is used in. + + Params: $id - Integer: Environment ID. + + Returns: Array: List of case-run object hashes. + +=item C + + Description: Returns the list of runs that this Environment is used in. + + Params: $id - Integer: Environment ID. + + Returns: Array: List of run object hashes. + +=item C + + Description: Performs a search and returns the resulting list of Environments + + Params: $query - Hash: keys must match valid search fields. + + +--------------------------+ + | classification | + | env_products | + | env_categories | + | env_elements | + | env_properties | + | env_expressions | + | name | + | env_value_selected_type | + +--------------------------+ + + Returns: Array: Matching Environments are retuned in a list of hashes. + +=item C + + Description: Updates the fields of the selected environment or environments. + + Params: $ids - Integer A single environment ID. + + $values - Hash of keys matching Environment fields and the new values + to set each field to. + +-------------+----------------+ + | Field | Type | + +-------------+----------------+ + | name | String | + | isactive | Boolean | + +-------------+----------------+ + + Returns: Hash: The updated environment object hash. + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks diff --git a/Bugzilla/WebService/Testopia/Product.pm b/Bugzilla/WebService/Testopia/Product.pm new file mode 100644 index 0000000..32fff63 --- /dev/null +++ b/Bugzilla/WebService/Testopia/Product.pm @@ -0,0 +1,423 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::Product; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Product; + +sub _validate { + my ($product) = @_; + Bugzilla->login(LOGIN_REQUIRED); + + if ($product =~ /^\d+$/){ + $product = Bugzilla::Testopia::Product->new($product); + } + else { + $product = Bugzilla::Product::check_product($product); + $product = Bugzilla::Testopia::Product->new($product->id); + } + + ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $product}) unless $product; + ThrowUserError('testopia-permission-denied', {'object' => $product}) if $product && !$product->canedit; + + return $product; +} + +sub get { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a product object hash + my $product = new Bugzilla::Testopia::Product($id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $id}) unless $product; + ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canedit; + + return $product; +} + +sub check_product { + my $self = shift; + my ($name) = @_; + + my $product = _validate($name); + + return $product; +} + +sub check_category { + my $self = shift; + my ($name, $product) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + $product = _validate($product); + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + require Bugzilla::Testopia::Category; + return Bugzilla::Testopia::Category->new(Bugzilla::Testopia::Category::check_case_category($name, $product)); +} + +sub check_component { + my $self = shift; + my ($name, $product) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + $product = _validate($product); + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + require Bugzilla::Component; + return Bugzilla::Component->check({product => $product, name => $name}); +} + +sub get_builds { + my $self = shift; + my ($product, $active) = @_; + + $product = _validate($product); + + return $product->builds($active); + +} + +sub get_category { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + require Bugzilla::Testopia::Category; + + my $category = Bugzilla::Testopia::Category->new($id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Category', id => $id}) unless $category; + ThrowUserError('testopia-permission-denied', {'object' => $category->product}) unless $category->product->canedit; + + delete $category->{'product'}; + + return $category; +} + +sub get_component { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + require Bugzilla::Component; + my $component = Bugzilla::Component->new($id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Component', id => $id}) unless $component; + + my $product = Bugzilla::Testopia::Product->new($component->product_id); + + ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canedit; + + return $component; +} + +sub get_cases { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->cases; +} + +sub get_categories { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->categories; +} + +sub get_components { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->components; +} + +sub get_environments { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->environments; +} + +sub get_milestones { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->milestones; +} + +sub get_plans { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->plans; +} + +sub get_runs { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->runs; +} + +sub get_tags { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->tags; +} + +sub get_versions { + my $self = shift; + my ($product) = @_; + + $product = _validate($product); + + return $product->versions; + +} + +sub lookup_name_by_id { + return {ERROR=> 'This method id deprecated. Use Product::get instead.'}; +} +sub lookup_id_by_name { + return {ERROR=> 'This method id deprecated. Use Product::check_product instead.'}; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::Product + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to expose Testopia Product data. + +=head1 METHODS + +=over + +=item C + + Description: Looks up and returns a category by name. + + Params: $name - String: name of the category. + $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Hash: Matching Category object hash or error if not found. + +=item C + + Description: Looks up and returns a component by name. + + Params: $name - String: name of the category. + $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Hash: Matching component object hash or error if not found. + +=item C + + Description: Looks up and returns a validated product. + + Params: $name - String: name of the product. + + Returns: Hash: Matching Product object hash or error if not found. + +=item C + + Description: Used to load an existing product from the database. + + Params: $id - An integer representing the ID in the database + + Returns: A blessed Bugzilla::Testopia::Product object hash + +=item C + + Description: Get the list of builds associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + $active - Boolean: True to only include builds where isactive is true. + + Returns: Array: Returns an array of Build objects. + +=item C + + Description: Get the list of cases associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of TestCase objects. + +=item C + + Description: Get the list of categories associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Case Category objects. + +=item C + + Description: Get the category matching the given id. + + Params: $id - Integer: ID of the category in the database. + + Returns: Hash: Category object hash. + +=item C + + Description: Get the component matching the given id. + + Params: $id - Integer: ID of the component in the database. + + Returns: Hash: Component object hash. + +=item C + + Description: Get the list of components associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Component objects. + +=item C + + Description: Get the list of environments associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Environment objects. + +=item C + + Description: Get the list of milestones associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Milestone objects. + +=item C + + Description: Get the list of plans associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Test Plan objects. + +=item C + + Description: Get the list of runs associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Test Run objects. + +=item C + + Description: Get the list of tags associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Tags objects. + +=item C + + Description: Get the list of versions associated with this product. + + Params: $product - Integer/String + Integer: product_id of the product in the Database + String: Product name + + Returns: Array: Returns an array of Version objects. + +=item C B Use Product::get instead + +=item C B Use Product::check_product instead + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/TestCase.pm b/Bugzilla/WebService/Testopia/TestCase.pm new file mode 100644 index 0000000..94c029e --- /dev/null +++ b/Bugzilla/WebService/Testopia/TestCase.pm @@ -0,0 +1,1141 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::TestCase; + +use strict; + +use Bugzilla::User; +use Bugzilla::Constants; +use Bugzilla::Error; + +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +use base qw(Bugzilla::WebService); + +sub get { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + $case->text(); + $case->author(); + $case->dependson_list(); + $case->blocked_list(); + + #Result is a test case object hash + return $case; +} + +sub list { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "case"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + return Bugzilla::Testopia::Table->new('case','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list(); +} + +sub list_count { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "case"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + return Bugzilla::Testopia::Table->new('case','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count(); +} + +sub create { + my $self = shift; + my ($values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @new_values; + if (ref $values eq 'ARRAY'){ + push @new_values, @$values; + } + else { + push @new_values, $values; + } + + my @results; + foreach my $new_values (@new_values){ + my @plan_ids; + if (ref $new_values->{'plans'} eq 'ARRAY'){ + push @plan_ids, @{$new_values->{'plans'}}; + } + else{ + push @plan_ids, split(/[\s,]+/, $new_values->{'plans'}); + } + + if ($new_values->{'plan_id'}){ + push @plan_ids, $new_values->{'plan_id'} + } + + my @plans; + eval{ + foreach my $id (@plan_ids){ + my $plan = Bugzilla::Testopia::TestPlan->new($id); + ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Plan'}) unless $plan; + ThrowUserError("testopia-create-denied", {'object' => 'Test Case', 'plan' => $plan}) unless $plan->canedit; + push @plans, $plan; + } + }; + if ($@){ + push @results, {ERROR => $@}; + next; + } + # Remove plan id from new_values hash + delete $new_values->{plan_id}; + + my @run_ids; + if (ref $new_values->{'runs'} eq 'ARRAY'){ + push @run_ids, @{$new_values->{'runs'}}; + } + my @bug_ids; + if (ref $new_values->{'bugs'} eq 'ARRAY'){ + push @bug_ids, @{$new_values->{'bugs'}}; + } + my @dependson; + if (ref $new_values->{'dependson'} eq 'ARRAY'){ + push @dependson, @{$new_values->{'dependson'}}; + } + my @blocks; + if (ref $new_values->{'blocks'} eq 'ARRAY'){ + push @blocks, @{$new_values->{'blocks'}}; + } + + $new_values->{'case_status_id'} ||= $new_values->{'status'}; + $new_values->{'priority_id'} ||= $new_values->{'priority'}; + $new_values->{'default_tester_id'} ||= $new_values->{'default_tester'}; + $new_values->{'category_id'} ||= $new_values->{'category'}; + $new_values->{'plans'} = \@plans; + $new_values->{'author_id'} ||= Bugzilla->user->id; + $new_values->{'runs'} = join(',', @run_ids) if scalar @run_ids; + $new_values->{'bugs'} = join(',', @bug_ids) if scalar @bug_ids; + $new_values->{'dependson'} = join(',', @dependson) if scalar @dependson; + $new_values->{'blocks'} = join(',', @blocks) if scalar @blocks; + + delete $new_values->{'default_tester'}; + delete $new_values->{'status'}; + delete $new_values->{'priority'}; + delete $new_values->{'category'}; + + my $case; + eval{ + $case = Bugzilla::Testopia::TestCase->create($new_values); + }; + if ($@){ + push @results, {ERROR => $@}; + } + else { + return $case if scalar @new_values == 1; + push @results, $case; + } + } + return \@results; +} + +sub update { + my $self = shift; + my ($ids, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($ids); + + my @dependson; + if (ref $new_values->{'dependson'} eq 'ARRAY'){ + push @dependson, @{$new_values->{'dependson'}}; + } + my @blocks; + if (ref $new_values->{'blocks'} eq 'ARRAY'){ + push @blocks, @{$new_values->{'blocks'}}; + } + $new_values->{'dependson'} = join(',', @dependson) if scalar @dependson; + $new_values->{'blocks'} = join(',', @blocks) if scalar @blocks; + + my @cases; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Case'}) if scalar @ids == 1; + push @cases, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + ThrowUserError('testopia-read-only', {'object' => $case}) if scalar @ids == 1; + push @cases, {ERROR => "You do not have rights to edit this test case"}; + next; + } + + $new_values->{'case_status_id'} ||= $new_values->{'status'}; + $new_values->{'priority_id'} ||= $new_values->{'priority'}; + $new_values->{'default_tester_id'} ||= $new_values->{'default_tester'}; + $new_values->{'category_id'} ||= $new_values->{'category'}; + + eval { + $case->set_case_status($new_values->{'case_status_id'}) if defined $new_values->{'case_status_id'}; + $case->set_category($new_values->{'category_id'}) if defined $new_values->{'category_id'}; + $case->set_priority($new_values->{'priority_id'}) if defined $new_values->{'priority_id'}; + $case->set_default_tester($new_values->{'default_tester_id'}) if defined $new_values->{'default_tester_id'}; + $case->set_sortkey($new_values->{'sortkey'}) if defined $new_values->{'sortkey'}; + $case->set_requirement($new_values->{'requirement'}) if defined $new_values->{'requirement'}; + $case->set_isautomated($new_values->{'isautomated'}) if defined $new_values->{'isautomated'}; + $case->set_script($new_values->{'script'}) if defined $new_values->{'script'}; + $case->set_arguments($new_values->{'arguments'}) if defined $new_values->{'arguments'}; + $case->set_summary($new_values->{'summary'}) if defined $new_values->{'summary'}; + $case->set_alias($new_values->{'alias'}) if defined $new_values->{'alias'}; + $case->set_estimated_time($new_values->{'estimated_time'}) if defined $new_values->{'estimated_time'}; + $case->set_dependson($new_values->{'dependson'}) if defined $new_values->{'dependson'}; + $case->set_blocks($new_values->{'blocks'}) if defined $new_values->{'blocks'}; + + $case->update; + $case->dependson_list; + $case->blocked_list; + }; + + if ($@){ + return $@ if (scalar @ids == 1); + push @cases, {ERROR => $@}; + } + else { + push @cases, $case; + } + + return $case if scalar @ids == 1; + } + + return \@cases; +} + +sub get_text { + my $self = shift; + my ($case_id, $version) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + #Result is the latest test case doc hash map + return $case->text($version); +} + +sub store_text { + my $self = shift; + my ($case_id, $action, $effect, $setup, $breakdown, $author_id,) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + $author_id ||= Bugzilla->user->id; + if ($author_id !~ /^\d+$/){ + $author_id = Bugzilla::User::login_to_id($author_id, "THROWERROR"); + } + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-read-only', {'object' => $case}) unless $case->canedit; + + my $version = $case->store_text($case_id, $author_id, $action, $effect, $setup, $breakdown); + + # Result is new test case doc version on success, otherwise an exception will be thrown + return $version; +} + +sub get_plans { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + return $case->plans(); +} + +sub attach_bug { + my $self = shift; + my ($case_ids, $bug_ids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->attach_bug($bug_ids); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub detach_bug { + my $self = shift; + my ($case_id, $bugids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-read-only', {'object' => $case}) unless $case->canedit; + + $case->detach_bug($bugids); + + # Result 0 on success, otherwise an exception will be thrown + return 0; +} + +sub get_bugs { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + # Result is list of bugs for the given test case + return $case->bugs; +} + +sub add_component { + my $self = shift; + my ($case_ids, $component_ids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->add_component($component_ids); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub remove_component { + my $self = shift; + my ($case_ids, $component_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->remove_component($component_id); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub get_components { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + # Result list of components otherwise an exception will be thrown + return $case->components(); +} + +sub add_tag { + my $self = shift; + my ($case_ids, $tags) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->add_tag($tags); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub remove_tag { + my $self = shift; + my ($case_ids, $tag_name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->remove_tag($tag_name); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub get_tags { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + my @results; + foreach my $tag (@{$case->tags}){ + push @results, $tag->name; + } + # Result list of tags otherwise an exception will be thrown + return \@results; +} + +sub link_plan { + my $self = shift; + my ($case_ids, $plan_ids) = @_; + Bugzilla->login(LOGIN_REQUIRED); + my @plans; + if (ref $plan_ids eq 'ARRAY'){ + $plan_ids = join(',', @$plan_ids); + } + foreach my $id (split(',', $plan_ids)){ + my $plan = Bugzilla::Testopia::TestPlan->new($id); + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + push @plans, $plan; + } + ThrowUserError('missing-plans-list') unless scalar @plans; + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + foreach my $plan (@plans){ + eval { + $case->link_plan($plan->id); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + } + + # Result is list of plans for test case on success, otherwise an exception will be thrown + return \@results; +} + +sub unlink_plan { + my $self = shift; + my ($case_id, $plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->can_unlink_plan($plan_id)); + + $case->unlink_plan($plan_id); + + # Result is list of plans for test case on success, otherwise an exception will be thrown + return $case->plans; +} + +sub add_to_run { + my $self = shift; + my ($case_ids, $run_ids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->add_to_run($run_ids); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub get_case_run_history { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + # Result list of caseruns otherwise an exception will be thrown + return $case->caseruns; +} + +sub get_change_history { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + # Result list of changes otherwise an exception will be thrown + return $case->history; +} + +sub calculate_average_time { + my $self = shift; + my ($case_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $case = new Bugzilla::Testopia::TestCase($case_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case', id => $case_id}) unless $case; + ThrowUserError('testopia-permission-denied', {'object' => $case}) unless $case->canview; + + return $case->calculate_average_time; +} + +sub lookup_category_id_by_name { + return { ERROR => 'This method is considered harmful and has been deprecated. Please use Testopia::Product::check_catagory instead'}; +} + +sub lookup_category_name_by_id { + return { ERROR => 'This method has been deprecated. Please use Testopia::Product::get_category instead'}; +} + +sub lookup_priority_id_by_name { + my $self = shift; + my ($name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test case priority id for the given test case priority name + return lookup_priority_by_value($name); +} + +sub lookup_priority_name_by_id { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + return lookup_priority($id); +} + +sub lookup_status_id_by_name { + my $self = shift; + my ($name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test case status id for the given test case status name + return lookup_status_by_name($name); +} + +sub lookup_status_name_by_id { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + return lookup_status($id); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::TestCase + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia TestCases + +=head1 METHODS + +=over + +=item C + + Description: Adds one or more components to the selected test cases. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an arry of case_ids or aliases, or a string of comma separated case_ids. + + $component_ids - Integer/Array/String - The component ID, an array of Component IDs or + component hashes (components can be an array of IDs, a comma separated string of IDs, + an array of Hashes, or a single hash where the + component hash = {component => 'string', product => 'string'}, + or a comma separated list of component IDs + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Add one or more tags to the selected test cases. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an arry of case_ids or aliases, or a string of comma separated case_ids. + + $tags - String/Array - A single tag, an array of tags, + or a comma separated list of tags. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Add one or more cases to the selected test runs. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an arry of case_ids or aliases, or a string of comma separated case_ids. + + $run_ids - Integer/Array/String: An integer representing the ID in the database + an array of IDs, or a comma separated list of IDs. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Add one or more bugs to the selected test cases. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of case_ids or aliases, or a string of comma separated case_ids. + + $bug_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of bug_ids or aliases, or a string of comma separated bug_ids. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Returns an average time for completion accross all runs. + + Params: $case_id - Integer/String: An integer or alias representing the ID in the database. + + Returns: String: Time in "HH:MM:SS" format. + +=item C + + Description: Creates a new Test Case object and stores it in the database. + + Params: $values - Array/Hash: A reference to a hash or array of hashes with keys and values + matching the fields of the test case to be created. + +-------------------+----------------+-----------+------------------------+ + | Field | Type | Null | Description | + +-------------------+----------------+-----------+------------------------+ + | status | Integer/String | Required | ID or Name of status | + | category* | Integer/Hash | Required | ID or hash | + | priority | Integer/String | Required | ID or Name of Priority | + | summary | String | Required | | + | plans | Array/Str/Int | Required | ID or List of plan_ids | + | default_tester | Integer/String | Optional | ID or Login of tester | + | estimated_time | String | Optional | HH:MM:SS Format | + | isautomated | Boolean | Optional | Defaults to False (0) | + | sortkey | Integer | Optional | | + | script | String | Optional | | + | arguments | String | Optional | | + | requirement | String | Optional | | + | alias | String | Optional | Must be unique | + | action | String | Optional | | + | effect | String | Optional | ExpectedResult | + | setup | String | Optional | | + | breakdown | String | Optional | | + | dependson | Array/String | Optional | String Comma separated | + | blocks | Array/String | Optional | String Comma separated | + | tags | Array/String | Optional | String Comma separated | + | bugs | Array/String | Optional | String Comma separated | + | components+ | Array/Hash/Str | Optional | String Comma separated | + +-------------------+----------------+-----------+------------------------+ + * category hash = {category => 'string', product => 'string'} + + components can be an array of IDs, a comma separated string of IDs, + an array of Hashes, or a single hash. + component hash = {component => 'string', product => 'string'} + + Returns: Array/Hash: The newly created object hash if a single case was created, or + an array of objects if more than one was created. If any single case threw an + error during creation, a hash with an ERROR key will be set in its place. + +=item C + + Description: Remove a bug from a test case. + + Params: $case_id - Integer/String: An integer or alias representing the ID in the database. + + $bug_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of bug_ids or aliases, or a string of comma separated bug_ids. + + Returns: 0 on success. + +=item C + + Description: Used to load an existing test case from the database. + + Params: $id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: A blessed Bugzilla::Testopia::TestCase object hash + +=item C + + Description: Get the list of bugs that are associated with this test case. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of bug object hashes. + +=item C + + Description: Get the list of case-runs for all runs this case appears in. + To limit this list by build or other attribute, see TestCaseRun::list. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of case-run object hashes. + +=item C + + Description: Get the list of changes to the fields of this case. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of hashes with changed fields and their details. + +=item C + + Description: Get the list of components attached to this case. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of component object hashes. + +=item C + + Description: Get the list of plans that this case is linked to. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of test plan object hashes. + +=item C + + Description: Get the list of tags attached to this case. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + Returns: Array: An array of tag object hashes. + +=item C + + Description: The associated large text fields: Action, Expected Results, Setup, Breakdown + for a given version. + + Params: $case_id - Integer/String: An integer representing the ID in the database + or a string representing the unique alias for this case. + + $version - Integer: (OPTIONAL) The version of the text you want returned. + Defaults to the latest. + + Returns: Hash: Text fields and values. + +=item C + + Description: Link test cases to the given plan. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of case_ids or aliases, or a string of comma separated case_ids. + + $plan_ids - Integer/Array/String: An integer representing the ID in the database, + an array of plan_ids, or a string of comma separated plan_ids. + + Returns: Array: Array of failure codes or an empty array. + +=item C + + Description: Performs a search and returns the resulting list of test cases. + + Params: $query - Hash: keys must match valid search fields. + + +--------------------------------------------------------+ + | Case Search Parameters | + +--------------------------------------------------------+ + | Key | Valid Values | + | andor | 1: Author AND tester, 0: OR | + | author | A bugzilla login (email address) | + | author_type | (select from email_variants) | + | case_id | comma separated integers | + | case_status | String: Status | + | case_status_id | Integer: Status | + | category | String: Category Name | + | category_id | Integer | + | component | String: Component Name | + | default_tester | A bugzilla login (email address) | + | default_tester_type | (select from email_variants) | + | isautomated | 1: true 0: false | + | plan_id | comma separated integers | + | priority | String: Priority | + | priority_id | Integer | + | product | String: Product Name | + | product_id | Integer | + | requirement | String: Requirement | + | requirement_type | (select from query_variants) | + | run_id | comma separated integers | + | script | String | + | script_type | (select from query_variants) | + | summary | String | + | summary_type | (select from query_variants) | + | tags | String | + | tags_type | (select from tag_variants) | + | tcaction | String | + | tcaction_type | (select from query_variants) | + | tceffect | String | + | tceffect_type | (select from query_variants) | + +--------------------------------------------------------+ + + +---------------------------------------------------+ + | Paging and Sorting | + +---------------------------------------------------+ + | Key | Description | + | dir | "ASC" or "DESC" | + | order | field to sort by | + +---------------------------------------------------+ + | page_size | integer: how many per page | + | page | integer: page number | + | +++++++ OR +++++++ | + | start | integer: Start with which record | + | limit | integer: limit to how many | + +---------------------------------------------------+ + | viewall | 1: returns all records | + +---------------------------------------------------+ + * The default is to only return 25 records at a time + + +----------------------------------------------------+ + | query_variants | + +----------------+-----------------------------------+ + | Key | Description | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | allwords | contains all of the words | + | anywords | contains any of the words | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + +----------------+-----------------------------------+ + + +-------------------------------------+ + | email_variants | + +--------------+----------------------+ + | Key | Description | + | substring | contains | + | exact | is | + | regexp | matches regexp | + | notregexp | doesn't match regexp | + +--------------+----------------------+ + + +----------------------------------------------------+ + | tag_variants | + +----------------+-----------------------------------+ + | Key | Description | + | anyexact | is tagged with | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + | allwords | contains all of the words | + | anywords | contains any of the words | + | nowords | contains none of the words | + +----------------------------------------------------+ + + Returns: Array: Matching test cases are retuned in a list of hashes. + +=item C + + Description: Performs a search and returns the resulting count of cases. + + Params: $query - Hash: keys must match valid search fields (see list). + + Returns: Integer - total matching cases. + +=item C B Use Testopia::Product::get_category instead + +=item C B Use Testopia::Product::check_category instead + +=item C + + Params: $id - Integer: ID of the case status to return + + Returns: String: the status name. + +=item C + + Params: $name - String: the status name. + + Returns: Integer: ID of the case status. + +=item C + + Params: $id - Integer: ID of the case status to return + + Returns: String: the status name. + +=item C + + Params: $name - String: the status name. + + Returns: Integer: ID of the case status. + +=item C + + Description: Removes selected component from the selected test case. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of case_ids or aliases, or a string of comma separated case_ids. + + $component_id - Integer: - The component ID to be removed. + + Returns: Array: Empty on success. + +=item C + + Description: Remove a tag from a case. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of case_ids or aliases, or a string of comma separated case_ids. + + $tag - String - A single tag to be removed. + + Returns: Array: Empty on success. + +=item C + + Description: Update the large text fields of a case. + + Params: $case_id - Integer: An integer or alias representing the ID in the database. + $action, $effect, $setup, $breakdown - String: Text for these fields. + [$author_id] = Integer/String: (OPTIONAL) The numeric ID or the login of the author. + Defaults to logged in user + + Returns: Integer: Version of the stored text + +=item C + + Description: Unlink a test case from the given plan. If only one plan is linked, this will delete + the test case. + + Params: $case_ids - Integer/String: An integer or alias representing the ID in the database. + + $plan_id - Integer: An integer representing the ID in the database. + + Returns: Array: Array of plans still linked if any, empty if not. + +=item C + + Description: Updates the fields of the selected case or cases. + + Params: $ids - Integer/String/Array + Integer: A single TestCase ID. + String: A comma separates string of TestCase IDs for batch + processing. + Array: An array of case IDs for batch mode processing + + $values - Hash of keys matching TestCase fields and the new values + to set each field to. + + Returns: Hash/Array: In the case of a single case it is returned. If a + list was passed, it returns an array of case hashes. If the + update on any particular case failed, the has will contain a + ERROR key and the message as to why it failed. + +-------------------+----------------+ + | Field | Type | + +-------------------+----------------+ + | status | Integer/String | + | category | Integer/String | + | priority | Integer/String | + | default_tester | Integer/String | + | estimated_time | String | + | isautomated | Boolean | + | sortkey | Integer | + | script | String | + | arguments | String | + | summary | String | + | requirement | String | + | alias | String | + | dependson | Array/String | + | blocks | Array/String | + +-------------------+----------------+ + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/TestCaseRun.pm b/Bugzilla/WebService/Testopia/TestCaseRun.pm new file mode 100644 index 0000000..158e4bd --- /dev/null +++ b/Bugzilla/WebService/Testopia/TestCaseRun.pm @@ -0,0 +1,714 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::TestCaseRun; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::User; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::Util; + +sub get { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + if ($build_id && $build_id !~ /^\d+$/){ + my $run = Bugzilla::Testopia::TestRun->new($run_id); + ThrowUserError('invalid-test-id-non-existent') unless $run; + my $build = Bugzilla::Testopia::Build::check_build($build_id, $run->product, "THROW"); + $build_id = $build->id; + } + if ($env_id && $env_id !~ /^\d+$/){ + my $run = Bugzilla::Testopia::TestRun->new($run_id); + ThrowUserError('invalid-test-id-non-existent') unless $run; + my $environment = Bugzilla::Testopia::Build::check_environment($env_id, $run->product, "THROW"); + $env_id = $environment->id; + } + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview; + + return $caserun; +} + +sub list { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "case_run"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + + return Bugzilla::Testopia::Table->new('case_run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list(); +} + +sub list_count { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "case_run"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + return Bugzilla::Testopia::Table->new('case_run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count(); +} + +sub create { + my $self = shift; + my ($new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $run = Bugzilla::Testopia::TestRun->new($new_values->{'run_id'}); + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $new_values->{'run_id'}}) unless $run; + ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit; + + $new_values->{'build_id'} ||= $new_values->{'build'}; + $new_values->{'environment_id'} ||= $new_values->{'environment'}; + + delete $new_values->{'build'}; + delete $new_values->{'environment'}; + + if (trim($new_values->{'build_id'}) !~ /^\d+$/ ){ + my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $run->plan->product, "THROWERROR"); + $new_values->{'build_id'} = $build->id; + } + if (trim($new_values->{'environment_id'}) !~ /^\d+$/ ){ + my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $run->plan->product, "THROWERROR"); + $new_values->{'environment_id'} = $environment->id; + } + + $new_values->{'case_run_status_id'} ||= $new_values->{'status'}; + $new_values->{'case_run_status_id'} ||= IDLE; + + delete $new_values->{'status'}; + + my $caserun = Bugzilla::Testopia::TestCaseRun->create($new_values); + + # Result is new test case run object hash + return $caserun; +} + +sub update { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @caseruns; + my @ids = Bugzilla::Testopia::Util::process_list($run_id); + if (ref $case_id eq 'HASH' && !$build_id){ + $new_values = $case_id; + foreach my $id (@ids){ + my $caserun = new Bugzilla::Testopia::TestCaseRun($id); + if ($caserun){ + push @caseruns, $caserun; + } + else { + push @caseruns, {ERROR => 'Case-run does not exist'}; + } + } + } + else { + foreach my $id (@ids){ + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id,$case_id,$build_id,$env_id); + if ($caserun){ + push @caseruns, $caserun; + } + else { + push @caseruns, {ERROR => 'Case-run does not exist'}; + } + } + } + + $new_values->{'case_run_status_id'} ||= $new_values->{'status'}; + $new_values->{'build_id'} ||= $new_values->{'build'}; + $new_values->{'environment_id'} ||= $new_values->{'environment'}; + + my @results; + + foreach my $caserun (@caseruns){ + if ($caserun->{'ERROR'}){ + push @results, $caserun; + next; + } + unless ($caserun->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + + $run_id = $caserun->run_id; + $case_id = $caserun->case_id; + $build_id ||= $caserun->build->id; + $env_id ||= $caserun->environment->id; + + # Check to see what has changed then use set methods + # The order is important. We need to check if the build or environment has + # Changed so that we can switch to the right record first. + if ($new_values->{'build_id'} && $new_values->{'environment_id'}){ + $caserun = $caserun->switch($new_values->{'build_id'}, $new_values->{'environment_id'}, $run_id, $case_id); + } + elsif ($new_values->{'build_id'}){ + $caserun = $caserun->switch($new_values->{'build_id'}, $env_id, $run_id, $case_id); + } + elsif ($new_values->{'environment_id'}){ + $caserun = $caserun->switch($build_id, $new_values->{'environment_id'}, $run_id, $case_id); + } + + # Now that we know we are working with the right record, update it. + if ($new_values->{'assignee'}){ + $caserun->set_assignee($new_values->{'assignee'}); + } + + if ($new_values->{'case_run_status_id'}){ + $caserun->set_status($new_values->{'case_run_status_id'}, $new_values->{'update_bugs'}); + } + + if ($new_values->{'sortkey'}){ + $caserun->set_sortkey($new_values->{'sortkey'}); + } + + if ($new_values->{'notes'}){ + $caserun->append_note($new_values->{'notes'}); + } + + # Remove assignee user object and replace with just assignee id + if (ref $caserun->{'assignee'} eq 'Bugzilla::User'){ + $caserun->{assignee} = $caserun->{assignee}->id(); + } + + # Result is modified test case run on success, otherwise an exception will be thrown + return $caserun if scalar @caseruns == 1; + push @results, $caserun; + } + return \@results; +} + +sub lookup_status_id_by_name { + my $self = shift; + my ($name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test case run status id for the given test case run status name + return Bugzilla::Testopia::TestCaseRun::lookup_status_by_name($name); +} + +sub lookup_status_name_by_id { + my $self = shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test case run status name for the given test case run status id + return Bugzilla::Testopia::TestCaseRun::lookup_status($id); +} + +sub get_history { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview; + + return $caserun->get_case_run_list; + +} + +sub attach_bug { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id, $bug_ids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + # If we have just the id, the third arg will not be set. + $bug_ids = $case_id unless $build_id; + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-read-only', {'object' => $caserun}) unless $caserun->canedit; + + $caserun->attach_bug($bug_ids); + + return undef; +} + +sub detach_bug { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id, $bug_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + # If we have just the id, the third arg will not be set. + $bug_id = $case_id unless $build_id; + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-read-only', {'object' => $caserun}) unless $caserun->canedit; + + $caserun->detach_bug($bug_id); + + # Result undef on success, otherwise an exception will be thrown + return undef; +} + +sub get_bugs { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview; + + return $caserun->bugs; +} + +sub get_completion_time { + my $self = shift; + my ($run_id, $case_id, $build_id, $env_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + #Result is a test case run hash map + my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun; + ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview; + + return $caserun->completion_time; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::TestCaseRun + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia TestCaseRuns. + +=head1 SYNOPSIS + +Test case-runs are the mapping of a test case in a given run for a particular +build and environment. There are therefore two ways to refer to a given +case-run: + + By ID: The unique case_run_id + By Combination: $run_id, $case_id, $build_id, $environment_id + +TestCaseRun methods are overloaded to support either of these two +methods of looking up the case-run you are interested in. + +B + +TestCaseRun->get($caserun_id) +TestCaseRun->get($run_id, $case_id, $build_id, $environment_id) + +=head1 METHODS + +=over + +=item C + + Description: Add one or more bugs to the selected test case-runs. + + Params: $case_run_id - Integer: An integer representing the ID in the database. + + $bug_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of bug_ids or aliases, or a string of comma separated bug_ids. + + Returns: undef. + +=item C + + Description: Add one or more bugs to the selected test case-runs. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + $bug_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of bug_ids or aliases, or a string of comma separated bug_ids. + + Returns: undef. + +=item C + + Description: Creates a new Test Case Run object and stores it in the database. + + Params: $values - Hash: A reference to a hash with keys and values + matching the fields of the test case to be created. + +--------------------+----------------+-----------+------------------------------------------------+ + | Field | Type | Null | Description | + +--------------------+----------------+-----------+------------------------------------------------+ + | run_id | Integer | Required | Test Run Number | + | case_id | Integer/String | Required | ID or alias of test case | + | build | Integer/String | Required | ID or name of a Build in plan's product | + | environment | Integer/String | Required | ID or name of an Environment in plan's product | + | assignee | Integer/String | Optional | Defaults to test case default tester | + | status | String | Optional | Defaults to "IDLE" | + | case_text_version | Integer | Optional | | + | notes | String | Optional | | + | sortkey | Integer | Optional | a.k.a. Index | + +--------------------+----------------+-----------+------------------------------------------------+ + Valid statuses include: IDLE, PASSED, FAILED, RUNNING, PAUSED, BLOCKED, ERROR + + Returns: The newly created object hash. + +=item C + + Description: Remove a bug from a test case-run. + + Params: $caserun_id - Integer: An integer representing the ID in the database. + + $bug_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an array of bug_ids or aliases, or a string of comma separated bug_ids. + + Returns: undef. + +=item C + + Description: Remove a bug from a test case-run. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + $bug_id - Integer: An integer or alias representing the ID of + the bug in the database, + + Returns: undef. + +=item C + + Description: Used to load an existing test case-run from the database. + + Params: $caserun_id - Integer: An integer representing the ID in + the database for this case-run. + + Returns: A blessed Bugzilla::Testopia::TestCaseRun object hash + +=item C + + Description: Used to load an existing test case from the database. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + Returns: A blessed Bugzilla::Testopia::TestCaseRun object hash + +=item C + + Description: Get the list of bugs that are associated with this test case. + + Params: $caserun_id - Integer: An integer representing the ID in + the database for this case-run. + + Returns: Array: An array of bug object hashes. + +=item C + + Description: Get the list of bugs that are associated with this test case. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + Returns: Array: An array of bug object hashes. + +=item C + + Description: Returns the time in seconds that it took for this case to complete. + + Params: $caserun_id - Integer: An integer representing the ID in + the database for this case-run. + + Returns: Integer: Seconds since run was started till this case was completed. + +=item C + + Description: Returns the time in seconds that it took for this case to complete. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + Returns: Integer: Seconds since run was started till this case was completed. + +=item C + + Description: Get the list of case-runs for all runs this case appears in. + To limit this list by build or other attribute, see TestCaseRun::list. + + Params: $caserun_id - Integer: An integer representing the ID in + the database for this case-run. + + Returns: Array: An array of case-run object hashes. + +=item C + + Description: Get the list of case-runs for all runs this case appears in. + To limit this list by build or other attribute, see TestCaseRun::list. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + Returns: Array: An array of case-run object hashes. + +=item C + + Description: Performs a search and returns the resulting list of test cases. + + Params: $query - Hash: keys must match valid search fields. + + +--------------------------------------------------------+ + | Case-Run Search Parameters | + +--------------------------------------------------------+ + | Key | Valid Values | + | andor | 1: Author AND tester, 0: OR | + | assignee | A bugzilla login (email address) | + | assignee_type | (select from email_variants) | + | build | String | + | build_id | Integer | + | case_id | comma separated integers | + | case_run_status | String: Status | + | case_run_status_id | Integer: Status | + | case_summary | String: Requirement | + | case_summary_type | (select from query_variants) | + | category | String: Category Name | + | category_id | Integer | + | component | String: Component Name | + | environment | String | + | environment_id | Integer | + | isactive | 0: Only show current 1: show all | + | isautomated | 1: true 0: false | + | milestone | String | + | notes | String | + | notes_type | (select from query_variants) | + | plan_id | comma separated integers | + | priority | String: Priority | + | priority_id | Integer | + | product | String | + | product_id | Integer | + | requirement | String: Requirement | + | requirement_type | (select from query_variants) | + | run_id | comma separated integers | + | run_product_version | String | + | run_status | 1: RUNNING 0: STOPPED | + | tags | String | + | tags_type | (select from tag_variants) | + | testedby | A bugzilla login (email address) | + | testedby_type | (select from email_variants) | + +--------------------------------------------------------+ + + +---------------------------------------------------+ + | Paging and Sorting | + +---------------------------------------------------+ + | Key | Description | + | dir | "ASC" or "DESC" | + | order | field to sort by | + +---------------------------------------------------+ + | page_size | integer: how many per page | + | page | integer: page number | + | +++++++ OR +++++++ | + | start | integer: Start with which record | + | limit | integer: limit to how many | + +---------------------------------------------------+ + | viewall | 1: returns all records | + +---------------------------------------------------+ + * The default is to only return 25 records at a time + + +----------------------------------------------------+ + | query_variants | + +----------------+-----------------------------------+ + | Key | Description | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | allwords | contains all of the words | + | anywords | contains any of the words | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + +----------------+-----------------------------------+ + + +-------------------------------------+ + | email_variants | + +--------------+----------------------+ + | Key | Description | + | substring | contains | + | exact | is | + | regexp | matches regexp | + | notregexp | doesn't match regexp | + +--------------+----------------------+ + + +----------------------------------------------------+ + | tag_variants | + +----------------+-----------------------------------+ + | Key | Description | + | anyexact | is tagged with | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + | allwords | contains all of the words | + | anywords | contains any of the words | + | nowords | contains none of the words | + +----------------------------------------------------+ + + Returns: Array: Matching test cases are retuned in a list of hashes. + +=item C + + Description: Performs a search and returns the resulting count of cases. + + Params: $query - Hash: keys must match valid search fields (see list). + + Returns: Integer - total matching cases. + +=item C + + Params: $id - Integer: ID of the status to return + + Returns: String: the status name. + +=item C + + Params: $name - String: the status name. + + Returns: Integer: ID of the status. + +=item C + + Description: Updates the fields of the selected case-runs. + + Params: $caserun_ids - Integer/String/Array + Integer: A single TestCaseRun ID. + String: A comma separates string of TestCaseRun IDs for batch + processing. + Array: An array of TestCaseRun IDs for batch mode processing + + $values - Hash of keys matching TestCaseRun fields and the new values + to set each field to. + +--------------------+----------------+ + | Field | Type | + +--------------------+----------------+ + | build | Integer/String | + | environment | Integer/String | + | assignee | Integer/String | + | status | String | + | notes | String | + | sortkey | Integer | + | update_bugs | Boolean | 1: Reopen bugs on FAILED 0: Don't change bug status + +--------------------+----------------+ + + Returns: Hash/Array: In the case of a single object, it is returned. If a + list was passed, it returns an array of object hashes. If the + update on any particular object failed, the hash will contain a + ERROR key and the message as to why it failed. + +=item C + + Description: Updates the fields of the selected case-run. + + Params: $case_id - Integer: An integer representing the ID of the test case in the database. + $run_id - Integer: An integer representing the ID of the test run in the database. + $build_id - Integer: An integer representing the ID of the test build in the database. + $environment_id - Integer: An integer representing the ID of the environment in the database. + + $values - Hash of keys matching TestCaseRun fields and the new values + to set each field to. See above. + + Returns: Hash/Array: In the case of a single object, it is returned. If a + list was passed, it returns an array of object hashes. If the + update on any particular object failed, the hash will contain a + ERROR key and the message as to why it failed. + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/TestPlan.pm b/Bugzilla/WebService/Testopia/TestPlan.pm new file mode 100644 index 0000000..646b7ba --- /dev/null +++ b/Bugzilla/WebService/Testopia/TestPlan.pm @@ -0,0 +1,584 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::TestPlan; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Constants; +use Bugzilla::User; +use Bugzilla::Util; +use Bugzilla::Error; + +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +sub get { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a plan object hash + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + $plan->test_run_count(); + $plan->test_case_count(); + + return $plan; +} + +sub list { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "plan"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + + return Bugzilla::Testopia::Table->new('plan','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list(); +} + +sub list_count { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "plan"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + return Bugzilla::Testopia::Table->new('plan','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count(); +} + +sub create { + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + $new_values->{'product_id'} ||= $new_values->{'product'}; + $new_values->{'type_id'} ||= $new_values->{'type'}; + delete $new_values->{'product'}; + delete $new_values->{'type'}; + + $new_values->{'author_id'} ||= Bugzilla->user->id; + + # Canedit check is performed in TestPlan::_check_product + my $plan = Bugzilla::Testopia::TestPlan->create($new_values); + + return $plan; +} + +sub update { + my $self =shift; + my ($plan_id, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit; + + $new_values->{'type_id'} ||= $new_values->{'type'}; + + $plan->set_name(trim($new_values->{'name'})); + $plan->set_default_product_version($new_values->{'default_product_version'}); + $plan->set_type($new_values->{'type_id'}); + $plan->set_isactive($new_values->{'isactive'}); + + $plan->update(); + + # Result is modified test plan, otherwise an exception will be thrown + return $plan; +} + +sub get_text { + my $self = shift; + my ($plan_id, $version) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + #Result is the latest test plan doc hash map + return $plan->text($version); +} + +sub store_text { + my $self = shift; + my ($plan_id, $text, $author_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + $author_id ||= Bugzilla->user->id; + if ($author_id !~ /^\d+$/){ + $author_id = Bugzilla::User::login_to_id($author_id, "THROWERROR"); + } + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit; + + my $version = $plan->store_text($plan_id, $author_id, $text); + + # Result is new test plan doc version on success, otherwise an exception will be thrown + return $version; +} + +sub get_test_cases { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a plan object hash + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + # Result is list of test cases for the given test plan + return $plan->test_cases; +} + +sub get_test_runs { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a plan object hash + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + # Result is list of test runs for the given test plan + return $plan->test_runs; +} + +sub get_change_history { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + # Result list of changes otherwise an exception will be thrown + return $plan->history; +} + +sub get_product { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a plan object hash + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + # Result is list of test cases for the given test plan + return $plan->product; +} + +sub lookup_type_name_by_id { + my $self =shift; + my ($id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test plan type name for the given test plan type id + return lookup_type($id); +} + +sub lookup_type_id_by_name { + my $self =shift; + my ($name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is test plan type id for the given test plan type name + return Bugzilla::Testopia::TestPlan::lookup_type_by_name($name); +} + +sub add_tag { + my $self = shift; + my ($plan_ids, $tags) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($plan_ids); + my @results; + foreach my $id (@ids){ + my $plan = new Bugzilla::Testopia::TestPlan($id); + unless ($plan){ + push @results, {ERROR => "TestPlan $id does not exist"}; + next; + } + unless ($plan->canedit){ + push @results, {ERROR => "You do not have rights to edit this test plan"}; + next; + } + eval { + $plan->add_tag($tags); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub remove_tag { + my $self = shift; + my ($plan_id, $tag_name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit; + + $plan->remove_tag($tag_name); + + # Result 0 on success, otherwise an exception will be thrown + return 0; +} + +sub get_tags { + my $self = shift; + my ($plan_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = new Bugzilla::Testopia::TestPlan($plan_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan; + ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview; + + my @results; + foreach my $tag (@{$plan->tags}){ + push @results, $tag->name; + } + # Result list of tags otherwise an exception will be thrown + return \@results; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::TestPlan + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia TestPlans + +=head1 METHODS + +=over + +=item C + + Description: Add one or more tags to the selected test plans. + + Params: $plan_ids - Integer/Array/String: An integer representing the ID of the plan in the database, + an arry of plan_ids, or a string of comma separated plan_ids. + + $tags - String/Array - A single tag, an array of tags, + or a comma separated list of tags. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Creates a new Test Plan object and stores it in the database. + + Params: $values - Hash: A reference to a hash with keys and values + matching the fields of the test plan to be created. + +-------------------------+----------------+-----------+------------------------------------+ + | Field | Type | Null | Description | + +-------------------------+----------------+-----------+------------------------------------+ + | product | Integer/String | Required | ID or Name of product | + | name | String | Required | | + | type | Integer/String | Required | ID or name of plan type | + | default_product_version | String | Required | | + | isactive | Boolean | Optional | 0: Archived 1: Active (Default 1) | + +-------------------------+----------------+-----------+------------------------------------+ + + Returns: The newly created object hash. + +=item C + + Description: Used to load an existing test plan from the database. + + Params: $id - Integer/String: An integer representing the ID of this plan in the database + + Returns: Hash: A blessed Bugzilla::Testopia::TestPlan object hash + +=item C + + Description: Get the list of changes to the fields of this plan. + + Params: $plan_id - Integer: An integer representing the ID of this plan in the database + + Returns: Array: An array of hashes with changed fields and their details. + +=item C + + Description: Get the Product the plan is assiciated with. + + Params: $plan_id - Integer: An integer representing the ID of the plan in the database. + + Returns: Hash: A blessed Bugzilla::Testopia::Product hash. + +=item C + + Description: Get the list of tags attached to this plan. + + Params: $plan_id - Integer An integer representing the ID of this plan in the database + + Returns: Array: An array of tag object hashes. + +=item C + + Description: Get the list of cases that this plan is linked to. + + Params: $plan_id - Integer: An integer representing the ID of the plan in the database + + Returns: Array: An array of test case object hashes. + +=item C + + Description: Get the list of runs in this plan. + + Params: $plan_id - Integer: An integer representing the ID of this plan in the database + + Returns: Array: An array of test run object hashes. + +=item C + + Description: The plan document for a given test plan. + + Params: $plan_id - Integer: An integer representing the ID of this plan in the database + + $version - Integer: (OPTIONAL) The version of the text you want returned. + Defaults to the latest. + + Returns: Hash: Text and author information. + +=item C + + Description: Performs a search and returns the resulting list of test plans. + + Params: $query - Hash: keys must match valid search fields. + + +--------------------------------------------------------+ + | Plan Search Parameters | + +--------------------------------------------------------+ + | Key | Valid Values | + | author | A bugzilla login (email address) | + | author_type | (select from email_variants) | + | plan_id | comma separated integers | + | plan_text | String | + | plan_text_type | (select from query_variants) | + | plan_type | String: Product Name | + | product | String: Product Name | + | product_id | Integer | + | tags | String | + | tags_type | (select from tag_variants) | + | type_id | Integer | + | version | String: Product version | + +--------------------------------------------------------+ + + +--------------------------------------------------------+ + | Paging and Sorting | + +--------------------------------------------------------+ + | Key | Description | + | dir | "ASC" or "DESC" | + | order | field to sort by | + +--------------------------------------------------------+ + | page_size | integer: how many per page | + | page | integer: page number | + | +++++++ OR +++++++ | + | start | integer: Start with which record | + | limit | integer: limit to how many | + +--------------------------------------------------------+ + | viewall | 1: returns all records 0: first 25 | + +--------------------------------------------------------+ + * The default is to only return 25 records at a time + + +----------------------------------------------------+ + | query_variants | + +----------------+-----------------------------------+ + | Key | Description | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | allwords | contains all of the words | + | anywords | contains any of the words | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + +----------------+-----------------------------------+ + + +-------------------------------------+ + | email_variants | + +--------------+----------------------+ + | Key | Description | + | substring | contains | + | exact | is | + | regexp | matches regexp | + | notregexp | doesn't match regexp | + +--------------+----------------------+ + + +----------------------------------------------------+ + | tag_variants | + +----------------+-----------------------------------+ + | Key | Description | + | anyexact | is tagged with | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + | allwords | contains all of the words | + | anywords | contains any of the words | + | nowords | contains none of the words | + +----------------------------------------------------+ + + Returns: Array: Matching test plans are retuned in a list of plan object hashes. + +=item C + + Description: Performs a search and returns the resulting count of plans. + + Params: $query - Hash: keys must match valid search fields (see list). + + Returns: Integer - total matching plans. + +=item C + + Params: $name - String: the plan type. + + Returns: Integer: ID of the plan type. + +=item C + + Params: $id - Integer: ID of the plan type to return + + Returns: String: the type name. + +=item C + + Description: Remove a tag from a plan. + + Params: $plan_id - Integer: An integer or alias representing the ID of this plan in the database. + + $tag - String - A single tag to be removed. + + Returns: 0 on success. + +=item C + + Description: Update the document field of a plan. + + Params: $plan_id - Integer: An integer representing the ID of this plan in the database. + $text - String: Text for the document. Can contain HTML. + [$author_id] = Integer/String: (OPTIONAL) The numeric ID or the login of the author. + Defaults to logged in user. + + Returns: Integer: The new text version + +=item C + + Description: Updates the fields of the selected test plan. + + Params: $ids - Integer: A single TestPlan ID. + + $values - Hash of keys matching TestPlan fields and the new values + to set each field to. + +-------------------------+----------------+ + | Field | Type | + +-------------------------+----------------+ + | name | String | + | type | Integer/String | + | default_product_version | String | + | isactive | Boolean | + +-------------------------+----------------+ + + Returns: Hash: The updated test plan object. + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/TestRun.pm b/Bugzilla/WebService/Testopia/TestRun.pm new file mode 100644 index 0000000..53cf9d1 --- /dev/null +++ b/Bugzilla/WebService/Testopia/TestRun.pm @@ -0,0 +1,725 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::TestRun; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Constants; +use Bugzilla::Product; +use Bugzilla::User; +use Bugzilla::Util; +use Bugzilla::Error; + +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +# Utility method called by the list method +sub get { + my $self = shift; + my ($run_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a run object hash + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + $run->{'case_count'} = $run->case_count(); + + return $run; +} + +sub list { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "run"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + + return Bugzilla::Testopia::Table->new('run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list(); +} + +sub list_count { + my $self = shift; + my ($query) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $cgi = Bugzilla->cgi; + + $cgi->param("current_tab", "run"); + + foreach (keys(%$query)){ + $cgi->param($_, $$query{$_}); + } + $cgi->param('distinct', 1); + + my $search = Bugzilla::Testopia::Search->new($cgi); + return Bugzilla::Testopia::Table->new('run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count(); +} + +sub create { + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $plan = Bugzilla::Testopia::TestPlan->new($new_values->{'plan_id'}); + ThrowUserError("testopia-create-denied", {'object' => 'Test Run', 'plan' => $plan}) unless ($plan->canedit); + + my @cases = Bugzilla::Testopia::Util::process_list($new_values->{'cases'}); + delete $new_values->{'cases'}; + + $new_values->{'manager_id'} ||= $new_values->{'manager'}; + $new_values->{'build_id'} ||= $new_values->{'build'}; + $new_values->{'environment_id'} ||= $new_values->{'environment'}; + + delete $new_values->{'manager'}; + delete $new_values->{'build'}; + delete $new_values->{'environment'}; + + $new_values->{'plan_text_version'} ||= $plan->version; + $new_values->{'product_version'} ||= $plan->product_version; + $new_values->{'status'} = 1 unless defined $new_values->{'status'} && $new_values->{'status'} == 0; + + if (trim($new_values->{'build_id'}) !~ /^\d+$/ ){ + my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $plan->product, "THROWERROR"); + $new_values->{'build_id'} = $build->id; + } + if (trim($new_values->{'environment_id'}) !~ /^\d+$/ ){ + my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $plan->product, "THROWERROR"); + $new_values->{'environment_id'} = $environment->id; + } + + my $run = Bugzilla::Testopia::TestRun->create($new_values); + + foreach my $c (@cases){ + my $case = Bugzilla::Testopia::TestCase->new($c); + $run->add_case_run($case->id, $case->sortkey) if $case; + } + + return $run; +} + +sub add_cases { + my $self = shift; + my ($case_ids, $run_ids) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($case_ids); + my @results; + foreach my $id (@ids){ + my $case = new Bugzilla::Testopia::TestCase($id); + unless ($case){ + push @results, {ERROR => "TestCase $id does not exist"}; + next; + } + unless ($case->canedit){ + push @results, {ERROR => "You do not have rights to edit this test case"}; + next; + } + eval { + $case->add_to_run($run_ids); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub update { + my $self =shift; + my ($run_id, $new_values) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run; + ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit; + + $new_values->{'manager_id'} ||= $new_values->{'manager'}; + $new_values->{'build_id'} ||= $new_values->{'build'}; + $new_values->{'environment_id'} ||= $new_values->{'environment'}; + + ThrowUserError("testopia-no-status", {field => 'status'}) if exists $new_values->{'status'} && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'manager'}) if $new_values->{'manager'} && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'target'}) if exists $new_values->{'target_pass'} && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'target'}) if exists $new_values->{'target_completion'} && !$run->canstatus; + + + if ($new_values->{'build_id'} && trim($new_values->{'build_id'}) !~ /^\d+$/ ){ + my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $run->plan->product, "THROWERROR"); + $new_values->{'build_id'} = $build->id; + } + if ($new_values->{'environment_id'} && trim($new_values->{'environment_id'}) !~ /^\d+$/ ){ + my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $run->plan->product, "THROWERROR"); + $new_values->{'environment_id'} = $environment->id; + } + + my $timestamp; + $timestamp = $run->stop_date; + $timestamp = undef if $new_values->{'status'} == 1; + $timestamp = Bugzilla::Testopia::Util::get_time_stamp() if $new_values->{'status'} == 0 && !$run->stop_date; + + $run->set_summary(trim($new_values->{'summary'})) if defined $new_values->{'summary'}; + $run->set_product_version($new_values->{'product_version'}) if $new_values->{'product_version'}; + $run->set_plan_text_version($new_values->{'plan_text_version'}) if $new_values->{'plan_text_version'}; + $run->set_build($new_values->{'build_id'}) if $new_values->{'build_id'}; + $run->set_environment($new_values->{'environment_id'}) if $new_values->{'environment_id'}; + $run->set_manager($new_values->{'manager_id'}) if $new_values->{'manager_id'}; + $run->set_notes($new_values->{'notes'}) if defined $new_values->{'notes'}; + $run->set_stop_date($timestamp) if exists $new_values->{'status'}; + $run->set_target_pass($new_values->{'target_pass'}) if defined $new_values->{'target_pass'}; + $run->set_target_completion($new_values->{'target_completion'}) if defined $new_values->{'target_completion'}; + + $run->update(); + + # Result is modified test run, otherwise an exception will be thrown + return $run; +} + +sub get_change_history { + my $self = shift; + my ($run_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + # Result list of changes otherwise an exception will be thrown + return $run->history; +} + +sub get_test_cases { + my $self = shift; + my ($run_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a run object hash + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + # Result is list of test cases for the given test run + return $run->cases; +} + +sub get_test_case_runs { + my $self = shift; + my ($run_id, $current) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a run object hash + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + # Result is list of test cases for the given test run + return $run->current_caseruns if $current; + return $run->caseruns; +} + +sub get_test_plan { + my $self = shift; + my ($run_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + # Result is a run object hash + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + # Result is list of test cases for the given test run + return $run->plan; +} + +sub lookup_environment_id_by_name { + return { ERROR => 'This method is considered harmful and has been deprecated. Please use Environment::check_environment instead'}; +} + +sub lookup_environment_name_by_id { + return { ERROR => 'This method has been deprecated. Please use Environment::get instead'}; +} + +sub add_tag { + my $self = shift; + my ($run_ids, $tags) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my @ids = Bugzilla::Testopia::Util::process_list($run_ids); + my @results; + foreach my $id (@ids){ + my $run = new Bugzilla::Testopia::TestRun($id); + unless ($run){ + push @results, {ERROR => "TestRun $id does not exist"}; + next; + } + unless ($run->canedit){ + push @results, {ERROR => "You do not have rights to edit this test run"}; + next; + } + eval { + $run->add_tag($tags); + }; + if ($@){ + push @results, {ERROR => $@}; + } + } + # @results will be empty if successful + return \@results; +} + +sub remove_tag { + my $self = shift; + my ($run_id, $tag_name) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run; + ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit; + + $run->remove_tag($tag_name); + + # Result 0 on success, otherwise an exception will be thrown + return 0; +} + +sub get_tags { + my $self = shift; + my ($run_id) = @_; + + Bugzilla->login(LOGIN_REQUIRED); + + my $run = new Bugzilla::Testopia::TestRun($run_id); + + ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run; + ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview; + + my @results; + foreach my $tag (@{$run->tags}){ + push @results, $tag->name; + } + # Result list of tags otherwise an exception will be thrown + return \@results; +} + +sub get_completion_report { + my $self = shift; + my ($runs) = @_; + my $vars; + + Bugzilla->login(LOGIN_REQUIRED); + + my @run_ids; + if (ref $runs eq 'ARRAY'){ + push @run_ids, @$runs + } + elsif ($runs =~ /,/){ + push @run_ids, split(/[\s,]+/, $runs); + } + else{ + push @run_ids, $runs; + } + + my @runs; + foreach my $g (@run_ids){ + my $obj = Bugzilla::Testopia::TestRun->new($g); + push @runs, $obj if $obj && $obj->canview; + } + + unless (scalar @runs){ + die "No runs found"; + } + + my $total = $runs[0]->case_run_count(undef, \@runs); + my $passed = $runs[0]->case_run_count(PASSED, \@runs); + my $failed = $runs[0]->case_run_count(FAILED, \@runs); + my $blocked = $runs[0]->case_run_count(BLOCKED, \@runs); + + my $completed = $passed + $failed + $blocked; + + my $unfinished = $total - $completed; + my $unpassed = $completed - $passed; + my $unfailed = $completed - $failed; + my $unblocked = $completed - $blocked; + + $vars->{'total'} = $total; + $vars->{'completed'} = $completed; + $vars->{'passed'} = $passed; + $vars->{'failed'} = $failed; + $vars->{'blocked'} = $blocked; + $vars->{'idle'} = $runs[0]->case_run_count(IDLE, \@runs); + $vars->{'running'} = $runs[0]->case_run_count(RUNNING, \@runs); + $vars->{'paused'} = $runs[0]->case_run_count(PAUSED, \@runs); + + $vars->{'percent_completed'} = calculate_percent($total, $completed); + $vars->{'percent_passed'} = calculate_percent($completed, $passed); + $vars->{'percent_failed'} = calculate_percent($completed, $failed); + $vars->{'percent_blocked'} = calculate_percent($completed, $blocked); + + return $vars; +} + +sub get_bugs { + my $self = shift; + my ($runs) = @_; + my $dbh = Bugzilla->dbh; + + my @run_ids = Bugzilla::Testopia::Util::process_list($runs); + + my $bugs = $dbh->selectcol_arrayref(" + SELECT DISTINCT tcb.bug_id + FROM test_case_bugs AS tcb + INNER JOIN test_case_runs AS tcr ON tcr.case_run_id = tcb.case_run_id + INNER JOIN bugs on tcb.bug_id = bugs.bug_id + INNER JOIN test_case_run_status AS tcrs ON tcr.case_run_status_id = tcrs.case_run_status_id + WHERE tcr.run_id in (" . join (',',@run_ids) . ") AND tcr.iscurrent = 1 ORDER BY tcb.bug_id"); + + my @bugs; + foreach my $id (@{$bugs}){ + push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id); + } + + return \@bugs; +} +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::TestRun + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides methods for automated scripts to manipulate Testopia TestRuns + +=head1 METHODS + +=over + +=item C + + Description: Add one or more cases to the selected test runs. + + Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, + an arry of case_ids or aliases, or a string of comma separated case_ids. + + $run_ids - Integer/Array/String: An integer representing the ID in the database + an array of IDs, or a comma separated list of IDs. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Add one or more tags to the selected test runs. + + Params: $run_ids - Integer/Array/String: An integer representing the ID in the database, + an arry of run_ids, or a string of comma separated run_ids. + + $tags - String/Array - A single tag, an array of tags, + or a comma separated list of tags. + + Returns: Array: empty on success or an array of hashes with failure + codes if a failure occured. + +=item C + + Description: Creates a new Test Run object and stores it in the database. + + Params: $values - Hash: A reference to a hash with keys and values + matching the fields of the test run to be created. + +-------------------+----------------+-----------+------------------------------------+ + | Field | Type | Null | Description | + +-------------------+----------------+-----------+------------------------------------+ + | plan_id | Integer | Required | ID of test plan | + | environment | Integer/String | Required | ID or Name of Environment | + | build | Integer/String | Required | ID or Name of Build | + | manager | Integer/String | Required | ID or Login of run manager | + | summary | String | Required | | + | product_version | String | Optional | Defaults to plan's version | + | plan_text_version | Integer | Optional | | + | target_completion | Integer | Optional | Targetted Completion percentage | + | target_pass | Integer | Optional | Targetted Pass percentage | + | notes | String | Optional | | + | status | Integer | Optional | 0:STOPPED 1: RUNNING (default 1) | + | cases | Array/String | Optional | list of case ids to add to the run | + +-------------------+----------------+-----------+------------------------------------+ + + Returns: The newly created object hash. + +=item C + + Description: Used to load an existing test run from the database. + + Params: $id - Integer: An integer representing the ID of the run in the database + + Returns: Hash: A blessed Bugzilla::Testopia::TestRun object hash + +=item C + + Description: Get the list of bugs attached to this run. + + Params: $runs - Integer/Array/String: An integer representing the ID in the database + an array of integers or a comma separated list of integers. + + Returns: Array: An array of bug object hashes. + +=item C + + Description: Get the list of changes to the fields of this run. + + Params: $run_id - Integer: An integer representing the ID of the run in the database + + Returns: Array: An array of hashes with changed fields and their details. + +=item C + + Description: Get a report of the current status of the selected runs combined. + + Params: $runs - Integer/Array/String: An integer representing the ID in the database + an array of integers or a comma separated list of integers. + + Returns: Hash: A hash containing counts and percentages of the combined totals of + case-runs in the run. Counts only the most recently statused case-run + for a given build and environment. + +=item C + + Description: Get the list of tags attached to this run. + + Params: $run_id - Integer: An integer representing the ID of the run in the database + + Returns: Array: An array of tags . + +=item C + + Description: Get the list of cases that this run is linked to. + + Params: $run_id - Integer: An integer representing the ID in the database + for this run. + + $current - Boolean: 1 to only include the current set (what is displayed + in the web page) 0: to return all, current and historical. + + Returns: Array: An array of test case-run object hashes. + +=item C + + Description: Get the list of cases that this run is linked to. + + Params: $run_id - Integer: An integer representing the ID in the database + for this run. + + Returns: Array: An array of test case object hashes. + +=item C + + Description: Get the plan that this run is associated with. + + Params: $run_id - Integer: An integer representing the ID in the database + for this run. + + Returns: Hash: A plan object hash. + +=item C + + Description: Performs a search and returns the resulting list of test runs. + + Params: $query - Hash: keys must match valid search fields. + + +--------------------------------------------------------+ + | Run Search Parameters | + +--------------------------------------------------------+ + | Key | Valid Values | + | build | String: Product Name | + | build_id | Integer | + | environment | String: Product Name | + | environment_id | Integer | + | manager | A bugzilla login (email address) | + | manager_type | (select from email_variants) | + | milestone | String | + | notes | String | + | notes_type | (select from query_variants) | + | plan_id | comma separated integers | + | product | String: Product Name | + | product_id | Integer | + | run_id | comma separated integers | + | run_status | 1: RUNNING 0: STOPPED | + | summary | String | + | summary_type | (select from query_variants) | + | tags | String | + | tags_type | (select from tag_variants) | + | type_id | Integer | + | version | String: Product version | + +--------------------------------------------------------+ + + +--------------------------------------------------------+ + | Paging and Sorting | + +--------------------------------------------------------+ + | Key | Description | + | dir | "ASC" or "DESC" | + | order | field to sort by | + +--------------------------------------------------------+ + | page_size | integer: how many per page | + | page | integer: page number | + | +++++++ OR +++++++ | + | start | integer: Start with which record | + | limit | integer: limit to how many | + +--------------------------------------------------------+ + | viewall | 1: returns all records 0: first 25 | + +--------------------------------------------------------+ + * The default is to only return 25 records at a time + + +----------------------------------------------------+ + | query_variants | + +----------------+-----------------------------------+ + | Key | Description | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | allwords | contains all of the words | + | anywords | contains any of the words | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + +----------------+-----------------------------------+ + + +-------------------------------------+ + | email_variants | + +--------------+----------------------+ + | Key | Description | + | substring | contains | + | exact | is | + | regexp | matches regexp | + | notregexp | doesn't match regexp | + +--------------+----------------------+ + + +----------------------------------------------------+ + | tag_variants | + +----------------+-----------------------------------+ + | Key | Description | + | anyexact | is tagged with | + | allwordssubstr | contains all of the words/strings | + | anywordssubstr | contains any of the words/strings | + | substring | contains the string | + | casesubstring | contains the string (exact case) | + | regexp | matches the regexp | + | notregexp | doesn't match the regexp | + | allwords | contains all of the words | + | anywords | contains any of the words | + | nowords | contains none of the words | + +----------------------------------------------------+ + + Returns: Array: Matching test runs are retuned in a list of run object hashes. + +=item C + + Description: Performs a search and returns the resulting count of runs. + + Params: $query - Hash: keys must match valid search fields (see list). + + Returns: Integer - total matching runs. + +=item C + + Description: Remove a tag from a run. + + Params: $run_id - Integer: An integer representing the ID in the database. + + $tag - String - A single tag to be removed. + + Returns: 0 on success. + +=item C + + Description: Updates the fields of the selected test run. + + Params: $ids - Integer: A single TestRun ID. + + $values - Hash of keys matching TestRun fields and the new values + to set each field to. See L for description + +-------------------+----------------+ + | Field | Type | + +-------------------+----------------+ + | plan_id | Integer | + | environment | Integer/String | + | build | Integer/String | + | manager | Integer/String | + | summary | String | + | product_version | String | + | plan_text_version | Integer | + | target_completion | Integer | + | target_pass | Integer | + | notes | String | + | status | Integer | + +-------------------+----------------+ + + Returns: Hash: The updated test run object. + +=back + +=head1 SEE ALSO + +L +L + +=head1 AUTHOR + +Greg Hendricks \ No newline at end of file diff --git a/Bugzilla/WebService/Testopia/Testopia.pm b/Bugzilla/WebService/Testopia/Testopia.pm new file mode 100644 index 0000000..eb4482f --- /dev/null +++ b/Bugzilla/WebService/Testopia/Testopia.pm @@ -0,0 +1,78 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Dallas Harken +# Greg Hendricks + +package Bugzilla::WebService::Testopia::Testopia; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Error; +use Bugzilla::Constants; + +sub api_version { + my $self = shift; + return "2.0"; +} + +sub testopia_version { + my $self = shift; + return "2.0-RC2"; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Webservice::Testopia + +=head1 EXTENDS + +Bugzilla::Webservice + +=head1 DESCRIPTION + +Provides information about this installation. + +=head1 METHODS + +=over + +=item C + + Description: Returns the API version. + +=item C + + Description: Returns the version of Testopia on this server. + +=back + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Greg Hendricks + diff --git a/extensions/testopia/code/db_schema-abstract_schema.pl b/extensions/testopia/code/db_schema-abstract_schema.pl new file mode 100644 index 0000000..8fdd783 --- /dev/null +++ b/extensions/testopia/code/db_schema-abstract_schema.pl @@ -0,0 +1,1100 @@ +#!/usr/bin/perl -wT + +use strict; +my $schema = Bugzilla->hook_args->{schema}; + +$schema->{test_attachments} = { + FIELDS => [ + attachment_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + submitter_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid' + } + }, + description => { TYPE => 'MEDIUMTEXT' }, + filename => { TYPE => 'MEDIUMTEXT' }, + creation_ts => { TYPE => 'DATETIME', NOTNULL => 1 }, + mime_type => { TYPE => 'varchar(100)', NOTNULL => 1 }, + ], + INDEXES => [ test_attachments_submitter_idx => ['submitter_id'], ], + }, + $schema->{test_case_attachments} = { + FIELDS => [ + attachment_id => { + TYPE => 'INT4', + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_attachments', + COLUMN => 'attachment_id', + DELETE => 'CASCADE' + } + }, + case_id => { + TYPE => 'INT4', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + case_run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_case_runs', + COLUMN => 'case_run_id', + DELETE => 'CASCADE' + } + + }, + ], + INDEXES => [ + test_case_attachments_primary_idx => ['attachment_id'], + attachment_case_id_idx => ['case_id'], + attachment_caserun_id_idx => ['case_run_id'], + ], + }, + $schema->{test_plan_attachments} = { + FIELDS => [ + attachment_id => { + TYPE => 'INT4', + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_attachments', + COLUMN => 'attachment_id', + DELETE => 'CASCADE' + } + }, + plan_id => { + TYPE => 'INT4', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + test_plan_attachments_primary_idx => ['attachment_id'], + attachment_plan_id_idx => ['plan_id'], + ], + }, + $schema->{test_case_categories} = { + FIELDS => [ + category_id => { TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'products', + COLUMN => 'id', + DELETE => 'CASCADE' + } + }, + name => { TYPE => 'varchar(240)', NOTNULL => 1 }, + description => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + category_product_id_name_idx => { FIELDS => [qw(product_id name)], TYPE => 'UNIQUE' }, + category_product_idx => { FIELDS => [qw(category_id product_id)], TYPE => 'UNIQUE' }, + category_name_idx_v2 => ['name'], + ], + }, + $schema->{test_cases} = { + FIELDS => [ + case_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + case_status_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_case_status', + COLUMN => 'case_status_id', + DELETE => 'CASCADE' + } + }, + category_id => { + TYPE => 'INT2', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_case_categories', + COLUMN => 'category_id', + DELETE => 'CASCADE' + } + }, + priority_id => { + TYPE => 'INT2', + REFERENCES => { + TABLE => 'priority', + COLUMN => 'id', + DELETE => 'CASCADE' + } + }, + author_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + } + }, + default_tester_id => { + TYPE => 'INT3', + }, + creation_date => { TYPE => 'DATETIME', NOTNULL => 1 }, + estimated_time => { TYPE => 'TIME' }, + isautomated => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0' }, + sortkey => { TYPE => 'INT4' }, + script => { TYPE => 'MEDIUMTEXT' }, + arguments => { TYPE => 'MEDIUMTEXT' }, + summary => { TYPE => 'varchar(255)' }, + requirement => { TYPE => 'varchar(255)' }, + alias => { TYPE => 'varchar(255)' }, + ], + INDEXES => [ + test_case_category_idx => ['category_id'], + test_case_author_idx => ['author_id'], + test_case_creation_date_idx => ['creation_date'], + test_case_sortkey_idx => ['sortkey'], + test_case_shortname_idx => ['alias'], + test_case_requirement_idx => ['requirement'], + test_case_status_idx => ['case_status_id'], + test_case_tester_idx => ['default_tester_id'], + ], + }, + $schema->{test_case_bugs} = { + FIELDS => [ + bug_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'bugs', + COLUMN => 'bug_id', + DELETE => 'CASCADE' + } + }, + case_run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_case_runs', + COLUMN => 'case_run_id', + DELETE => 'CASCADE' + } + }, + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + case_bugs_bug_id_idx => ['bug_id'], + case_bugs_case_id_idx => ['case_id'], + case_bugs_case_run_id_idx => ['case_run_id'], + ], + }, + $schema->{test_case_runs} = { + FIELDS => [ + case_run_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_runs', + COLUMN => 'run_id', + DELETE => 'CASCADE' + } + }, + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + assignee => { + TYPE => 'INT3' + }, + testedby => { + TYPE => 'INT3' + }, + case_run_status_id => { + TYPE => 'INT2', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_case_run_status', + COLUMN => 'case_run_status_id', + DELETE => 'CASCADE' + } + }, + case_text_version => { TYPE => 'INT3', NOTNULL => 1 }, + build_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_builds', + COLUMN => 'build_id', + DELETE => 'CASCADE' + } + }, + running_date => { TYPE => 'DATETIME' }, + close_date => { TYPE => 'DATETIME' }, + notes => { TYPE => 'TEXT' }, + iscurrent => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0' }, + sortkey => { TYPE => 'INT4' }, + environment_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environments', + COLUMN => 'environment_id', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + case_run_case_id_idx => ['case_id'], + case_run_assignee_idx => ['assignee'], + case_run_testedby_idx => ['testedby'], + case_run_close_date_idx => ['close_date'], + case_run_build_env_idx => { + FIELDS => [qw(run_id case_id build_id environment_id)], + TYPE => 'UNIQUE' + }, + case_run_status_idx => ['case_run_status_id'], + case_run_text_ver_idx => ['case_text_version'], + case_run_build_idx_v2 => ['build_id'], + case_run_env_idx_v2 => ['environment_id'], + ], + }, + $schema->{test_case_texts} = { + FIELDS => [ + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + case_text_version => { TYPE => 'INT3', NOTNULL => 1 }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + }, + creation_ts => { TYPE => 'DATETIME', NOTNULL => 1 }, + action => { TYPE => 'MEDIUMTEXT' }, + effect => { TYPE => 'MEDIUMTEXT' }, + setup => { TYPE => 'MEDIUMTEXT' }, + breakdown => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + case_versions_idx => { + FIELDS => [qw(case_id case_text_version)], + TYPE => 'UNIQUE' + }, + case_versions_who_idx => ['who'], + case_versions_creation_ts_idx => ['creation_ts'], + ], + }, + $schema->{test_tags} = { + FIELDS => [ + tag_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + tag_name => { TYPE => 'varchar(255)', NOTNULL => 1 }, + ], + INDEXES => [ test_tag_name_idx_v2 => [qw(tag_name)] ], + }, + $schema->{test_case_tags} = { + FIELDS => [ + tag_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_tags', + COLUMN => 'tag_id', + DELETE => 'CASCADE' + } + }, + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + userid => { + TYPE => 'INT3', + NOTNULL => 1, + }, + ], + INDEXES => [ + case_tags_primary_idx => { FIELDS => [qw(tag_id case_id userid)], TYPE => 'UNIQUE' }, + case_tags_secondary_idx => { FIELDS => [qw(tag_id case_id)], TYPE => 'UNIQUE' }, + case_tags_case_id_idx_v3 => [qw(case_id)], + case_tags_userid_idx => [qw(userid)], + ], + }, + $schema->{test_run_tags} = { + FIELDS => [ + tag_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_tags', + COLUMN => 'tag_id', + DELETE => 'CASCADE' + } + }, + run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_runs', + COLUMN => 'run_id', + DELETE => 'CASCADE' + } + + }, + userid => { + TYPE => 'INT3', + NOTNULL => 1, + }, + ], + INDEXES => [ + run_tags_primary_idx => { FIELDS => [qw(tag_id run_id userid)], TYPE => 'UNIQUE' }, + run_tags_secondary_idx => { FIELDS => [qw(tag_id run_id)], TYPE => 'UNIQUE' }, + run_tags_run_id_idx => [qw(run_id)], + run_tags_userid_idx => [qw(userid)], + ], + }, + $schema->{test_plan_tags} = { + FIELDS => [ + tag_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_tags', + COLUMN => 'tag_id', + DELETE => 'CASCADE' + } + }, + plan_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + userid => { + TYPE => 'INT3', + NOTNULL => 1, + }, + ], + INDEXES => [ + plan_tags_primary_idx => { FIELDS => [qw(tag_id plan_id userid)], TYPE => 'UNIQUE' }, + plan_tags_secondary_idx => { FIELDS => [qw(tag_id plan_id)], TYPE => 'UNIQUE' }, + plan_tags_plan_id_idx => [qw(plan_id)], + plan_tags_userid_idx => [qw(userid)], + ], + }, + $schema->{test_plans} = { + FIELDS => [ + plan_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'products', + COLUMN => 'id', + DELETE => 'CASCADE' + } + + }, + author_id => { + TYPE => 'INT3', + NOTNULL => 1, + }, + type_id => { + TYPE => 'INT2', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_plan_types', + COLUMN => 'type_id', + DELETE => 'CASCADE' + } + }, + default_product_version => { TYPE => 'MEDIUMTEXT', NOTNULL => 1 }, + name => { TYPE => 'varchar(255)', NOTNULL => 1 }, + creation_date => { TYPE => 'DATETIME', NOTNULL => 1 }, + isactive => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1' }, + ], + INDEXES => [ + plan_product_plan_id_idx => [qw(product_id plan_id)], + plan_author_idx => ['author_id'], + plan_type_idx => ['type_id'], + plan_isactive_idx => ['isactive'], + plan_name_idx => ['name'], + ], + }, + $schema->{test_plan_permissions} = { + FIELDS => [ + userid => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE' + } + + }, + plan_id => { + TYPE => 'INT4', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + + }, + permissions => { TYPE => 'INT1', NOTNULL => 1 }, + grant_type => { TYPE => 'INT1', NOTNULL => 1 }, + ], + INDEXES => [ + testers_plan_user_idx => { FIELDS => [qw(userid plan_id grant_type)], TYPE => 'UNIQUE' }, + testers_plan_user_plan_idx => ['plan_id'], + testers_plan_grant_idx => ['grant_type'], + ], + }, + $schema->{test_plan_permissions_regexp} = { + FIELDS => [ + plan_id => { + TYPE => 'INT4', + NOTNULL => 1, + UNSIGNED => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + user_regexp => { TYPE => 'TEXT', NOTNULL => 1 }, + permissions => { TYPE => 'INT1', NOTNULL => 1 }, + ], + INDEXES => [ testers_plan_regexp_idx => { FIELDS => [qw(plan_id)], TYPE => 'UNIQUE' }, ], + }, + $schema->{test_plan_texts} = { + FIELDS => [ + plan_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + plan_text_version => { TYPE => 'INT4', NOTNULL => 1 }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + }, + creation_ts => { TYPE => 'DATETIME', NOTNULL => 1 }, + plan_text => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + test_plan_text_version_idx => [qw(plan_id plan_text_version)], + test_plan_text_who_idx => ['who'], + ], + }, + + # Tiny table -- don't add keys besides primary key. + $schema->{test_plan_types} = { + FIELDS => [ + type_id => { TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(64)', NOTNULL => 1 }, + description => { TYPE => 'MEDIUMTEXT' }, + ], + }, + $schema->{test_runs} = { + FIELDS => [ + run_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + plan_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + environment_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environments', + COLUMN => 'environment_id', + DELETE => 'CASCADE' + } + }, + product_version => { TYPE => 'MEDIUMTEXT' }, + build_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_builds', + COLUMN => 'build_id', + DELETE => 'CASCADE' + } + }, + plan_text_version => { TYPE => 'INT4', NOTNULL => 1 }, + manager_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE' + } + }, + default_tester_id => { + TYPE => 'INT3', + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE' + } + }, + start_date => { TYPE => 'DATETIME', NOTNULL => 1 }, + stop_date => { TYPE => 'DATETIME' }, + summary => { TYPE => 'TINYTEXT', NOTNULL => 1 }, + notes => { TYPE => 'MEDIUMTEXT' }, + target_pass => { TYPE => 'INT1' }, + target_completion => { TYPE => 'INT1' }, + + ], + INDEXES => [ + test_run_plan_id_run_id_idx => [qw(plan_id run_id)], + test_run_manager_idx => ['manager_id'], + test_run_start_date_idx => ['start_date'], + test_run_stop_date_idx => ['stop_date'], + test_run_env_idx => ['environment_id'], + test_run_build_idx => ['build_id'], + test_run_plan_ver_idx => ['plan_text_version'], + test_run_tester_idx => ['default_tester_id'], + ], + }, + $schema->{test_case_plans} = { + FIELDS => [ + plan_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + test_case_plans_primary_idx => { FIELDS => [qw(plan_id case_id)], TYPE => 'UNIQUE' }, + test_case_plans_case_idx => [qw(case_id)], + ], + }, + $schema->{test_case_activity} = { + FIELDS => [ + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + fieldid => { + TYPE => 'INT2', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_fielddefs', + COLUMN => 'fieldid', + DELETE => 'CASCADE' + } + }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + } + }, + changed => { TYPE => 'DATETIME', NOTNULL => 1 }, + oldvalue => { TYPE => 'MEDIUMTEXT' }, + newvalue => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + case_activity_case_id_idx => ['case_id'], + case_activity_who_idx => ['who'], + case_activity_when_idx => ['changed'], + case_activity_field_idx => ['fieldid'], + ], + }, + + # Tiny table -- don't add keys besides primary key. + $schema->{test_fielddefs} = { + FIELDS => [ + fieldid => { TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(100)', NOTNULL => 1 }, + description => { TYPE => 'MEDIUMTEXT' }, + table_name => { TYPE => 'varchar(100)', NOTNULL => 1 }, + ], + }, + $schema->{test_plan_activity} = { + FIELDS => [ + plan_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_plans', + COLUMN => 'plan_id', + DELETE => 'CASCADE' + } + }, + fieldid => { + TYPE => 'INT2', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_fielddefs', + COLUMN => 'fieldid', + DELETE => 'CASCADE' + } + }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + } + }, + changed => { TYPE => 'DATETIME', NOTNULL => 1 }, + oldvalue => { TYPE => 'MEDIUMTEXT' }, + newvalue => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + plan_activity_primary_idx => ['plan_id'], + plan_activity_field_idx => ['fieldid'], + plan_activity_who_idx => ['who'], + plan_activity_changed_idx => ['changed'], + ], + }, + $schema->{test_case_components} = { + FIELDS => [ + case_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_cases', + COLUMN => 'case_id', + DELETE => 'CASCADE' + } + }, + component_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'components', + COLUMN => 'id', + DELETE => 'CASCADE' + } + + }, + ], + INDEXES => [ + components_case_id_idx => { FIELDS => [qw(case_id component_id)], TYPE => 'UNIQUE' }, + components_component_id_idx => ['component_id'], + ], + }, + $schema->{test_run_activity} = { + FIELDS => [ + run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_runs', + COLUMN => 'run_id', + DELETE => 'CASCADE' + } + }, + fieldid => { + TYPE => 'INT2', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_fielddefs', + COLUMN => 'fieldid', + DELETE => 'CASCADE' + } + }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + } + }, + changed => { TYPE => 'DATETIME', NOTNULL => 1 }, + oldvalue => { TYPE => 'MEDIUMTEXT' }, + newvalue => { TYPE => 'MEDIUMTEXT' }, + ], + INDEXES => [ + run_activity_run_id_idx => ['run_id'], + run_activity_field_idx => ['fieldid'], + run_activity_who_idx => ['who'], + run_activity_when_idx => ['changed'], + ], + }, + $schema->{test_run_cc} = { + FIELDS => [ + run_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_runs', + COLUMN => 'run_id', + DELETE => 'CASCADE' + } + }, + who => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + test_run_cc_primary_idx => { FIELDS => [qw(run_id who)], TYPE => 'UNIQUE' }, + test_run_cc_who_idx => [qw(who)], + ], + }, + $schema->{test_email_settings} = { + FIELDS => [ + userid => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + } + }, + eventid => { + TYPE => 'INT1', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_events', + COLUMN => 'eventid', + DELETE => 'CASCADE' + } + }, + relationship_id => { + TYPE => 'INT1', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_relationships', + COLUMN => 'relationship_id', + DELETE => 'CASCADE' + } + }, + ], + INDEXES => [ + test_email_setting_user_id_idx => { + FIELDS => [qw(userid relationship_id eventid)], + TYPE => 'UNIQUE' + }, + ], + }, + $schema->{test_events} = { + FIELDS => [ + eventid => { TYPE => 'INT1', UNSIGNED => 1, PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(50)' }, + ], + INDEXES => [ test_event_name_idx => ['name'], ], + }, + $schema->{test_relationships} = { + FIELDS => [ + relationship_id => { TYPE => 'INT1', UNSIGNED => 1, PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(50)' }, + ], + }, + + # Tiny table -- don't add keys besides primary key. + $schema->{test_case_run_status} = { + FIELDS => [ + case_run_status_id => { TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(20)' }, + sortkey => { TYPE => 'INT4' }, + description => { TYPE => 'TEXT' }, + ], + }, + + # Tiny table -- don't add keys besides primary key. + $schema->{test_case_status} = { + FIELDS => [ + case_status_id => { TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + name => { TYPE => 'varchar(255)', NOTNULL => 1 }, + description => { TYPE => 'TEXT' }, + ], + }, + $schema->{test_case_dependencies} = { + FIELDS => [ + dependson => { TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1 }, + blocked => { TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1 }, + ], + INDEXES => [ + case_dependencies_primary_idx => { FIELDS => [qw(dependson blocked)], TYPE => 'UNIQUE' }, + case_dependencies_blocked_idx => ['blocked'], + ], + }, + $schema->{test_environments} = { + FIELDS => [ + environment_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'products', + COLUMN => 'id', + DELETE => 'CASCADE' + } + }, + name => { TYPE => 'varchar(255)' }, + isactive => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1' }, + ], + INDEXES => [ + test_environments_key1 => { FIELDS => [qw(environment_id product_id)], TYPE => 'UNIQUE' }, + test_environments_key2 => { FIELDS => [qw(product_id name)], TYPE => 'UNIQUE' }, + environment_name_idx_v2 => ['name'], + ], + }, + $schema->{test_builds} = { + FIELDS => [ + build_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => { + TABLE => 'products', + COLUMN => 'id', + DELETE => 'CASCADE' + } + }, + milestone => { TYPE => 'varchar(20)' }, + name => { TYPE => 'varchar(255)' }, + description => { TYPE => 'TEXT' }, + isactive => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1' }, + ], + INDEXES => [ + build_name_idx => ['name'], + build_milestone_idx => ['milestone'], + build_product_id_name_idx => { FIELDS => [qw(product_id name)], TYPE => 'UNIQUE' }, + build_prod_idx => { FIELDS => [qw(build_id product_id)], TYPE => 'UNIQUE' }, + ], + }, + $schema->{test_attachment_data} = { + FIELDS => [ + attachment_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_attachments', + COLUMN => 'attachment_id', + DELETE => 'CASCADE' + } + }, + contents => { TYPE => 'LONGBLOB' }, + ], + INDEXES => [ test_attachment_data_primary_idx => ['attachment_id'], ], + }, + $schema->{test_named_queries} = { + FIELDS => [ + userid => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { + TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE' + } + }, + name => { TYPE => 'varchar(64)', NOTNULL => 1 }, + isvisible => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1 }, + query => { TYPE => 'MEDIUMTEXT', NOTNULL => 1 }, + type => { TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0 }, + ], + INDEXES => [ + test_namedquery_primary_idx => { FIELDS => [qw(userid name)], TYPE => 'UNIQUE' }, + test_namedquery_name_idx => ['name'], + ], + }, + $schema->{test_environment_map} = { + FIELDS => [ + environment_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environments', + COLUMN => 'environment_id', + DELETE => 'CASCADE' + } + }, + property_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environment_property', + COLUMN => 'property_id', + DELETE => 'CASCADE' + } + }, + element_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environment_element', + COLUMN => 'element_id', + DELETE => 'CASCADE' + } + }, + value_selected => { TYPE => 'TINYTEXT' }, + ], + INDEXES => [ + env_map_env_element_idx => [qw(environment_id element_id)], + env_map_property_idx => [qw(environment_id property_id)], + test_environment_map_key3 => { FIELDS => [qw(environment_id element_id property_id)], TYPE => 'UNIQUE' }, + ], + }, + $schema->{test_environment_element} = { + FIELDS => [ + element_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + env_category_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environment_category', + COLUMN => 'env_category_id', + DELETE => 'CASCADE' + } + }, + name => { TYPE => 'varchar(255)' }, + parent_id => { TYPE => 'INT4', UNSIGNED => 1 }, + isprivate => { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0 }, + ], + INDEXES => [ + test_environment_element_key1 => { FIELDS => [qw(element_id env_category_id)], TYPE => 'UNIQUE' }, + test_environment_element_key2 => { FIELDS => [qw(env_category_id name)], TYPE => 'UNIQUE' }, + ], + }, + $schema->{test_environment_category} = { + FIELDS => [ + env_category_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + }, + name => { TYPE => 'varchar(255)' }, + ], + INDEXES => [ + test_environment_category_key1 => { FIELDS => [qw(env_category_id product_id)], TYPE => 'UNIQUE' }, + test_environment_category_key2 => { FIELDS => [qw(product_id name)], TYPE => 'UNIQUE' }, + ], + }, + $schema->{test_environment_property} = { + FIELDS => [ + property_id => { TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1 }, + element_id => { + TYPE => 'INT4', + UNSIGNED => 1, + NOTNULL => 1, + REFERENCES => { + TABLE => 'test_environment_element', + COLUMN => 'element_id', + DELETE => 'CASCADE' + } + }, + name => { TYPE => 'varchar(255)' }, + validexp => { TYPE => 'TEXT' }, + ], + INDEXES => [ + test_environment_property_key1 => { FIELDS => [qw(property_id element_id)], TYPE => 'UNIQUE' }, + test_environment_property_key2 => { FIELDS => [qw(element_id name)], TYPE => 'UNIQUE' }, + ], + }, diff --git a/extensions/testopia/code/enter_bug-entrydefaultvars.pl b/extensions/testopia/code/enter_bug-entrydefaultvars.pl new file mode 100644 index 0000000..9144076 --- /dev/null +++ b/extensions/testopia/code/enter_bug-entrydefaultvars.pl @@ -0,0 +1,8 @@ +#!/usr/bin/perl -w + +use strict; +my $vars = Bugzilla->hook_args->{vars}; +my $cgi = Bugzilla->cgi; + +$vars->{'case_id'} = $cgi->param('case_id'); +$vars->{'caserun_id'} = $cgi->param('caserun_id'); \ No newline at end of file diff --git a/extensions/testopia/code/install-before_final_checks.pl b/extensions/testopia/code/install-before_final_checks.pl new file mode 100644 index 0000000..30a031f --- /dev/null +++ b/extensions/testopia/code/install-before_final_checks.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2009 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib '.'; +use Bugzilla; +use Bugzilla::User::Setting; + +add_setting('view_testopia', ['on', 'off'], 'on'); + diff --git a/extensions/testopia/code/install-requirements.pl b/extensions/testopia/code/install-requirements.pl new file mode 100644 index 0000000..313c2cc --- /dev/null +++ b/extensions/testopia/code/install-requirements.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w + +sub REQUIRED_MODULES { + my @modules = ( + { + package => 'JSON', + module => 'JSON', + version => '2.10' + }, + { + package => 'Text-Diff', + module => 'Text::Diff', + version => '0.35' + }, + { + package => 'GD-Graph3d', + module => 'GD::Graph3d', + version => '0.63' + }, + ); + return \@modules; +}; + +sub OPTIONAL_MODULES { + my @modules = ( + { + package => 'Text-CSV', + module => 'Text::CSV', + version => '1.06', + feature => 'CSV Importing of test cases' + } + ); + + return \@modules; +}; + \ No newline at end of file diff --git a/extensions/testopia/code/install-update_db.pl b/extensions/testopia/code/install-update_db.pl new file mode 100644 index 0000000..ee371e0 --- /dev/null +++ b/extensions/testopia/code/install-update_db.pl @@ -0,0 +1,559 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Maciej Maczynski +# Ed Fuentetaja +# Vance Baarda + +use strict; +use lib '.'; +use Bugzilla; +use Bugzilla::Group; + +# Start of main(). +print "\nChecking Testopia setup ...\n"; +testopiaUpdateDB(); +updateACLs(); +migrateAttachments(); +createGroup(); +finalFixups(); +print "Done checking Testopia setup.\n\n"; +# End of main(). + +sub testopiaUpdateDB { + my $dbh = Bugzilla->dbh; + + # If the database contains Testopia tables but bz_schema doesn't + # know about them, then we need to update bz_schema. + if (grep(/^test_cases$/, $dbh->bz_table_list_real) and + !$dbh->_bz_real_schema->get_table_abstract('test_cases')) { + my $msg = "Sorry, we cannot upgrade from Testopia 1.0 using this " . + "database. Upgrades are supported only with MySQL."; + die($msg) unless $dbh->isa('Bugzilla::DB::Mysql'); + my $built_schema = $dbh->_bz_build_schema_from_disk; + foreach my $table (grep(/^test_/, $built_schema->get_table_list())) { + $dbh->_bz_real_schema->add_table($table, + $built_schema->get_table_abstract($table)); + } + $dbh->_bz_store_real_schema; + } + + $dbh->bz_setup_database(); + + $dbh->bz_drop_table('test_case_group_map'); + $dbh->bz_drop_table('test_category_templates'); + $dbh->bz_drop_table('test_plan_testers'); + $dbh->bz_drop_table('test_plan_group_map'); + $dbh->bz_drop_column('test_plans', 'editor_id'); + + $dbh->bz_add_column('test_case_bugs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1}); + $dbh->bz_add_column('test_case_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, 0); + $dbh->bz_add_column('test_case_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0); + $dbh->bz_add_column('test_case_texts', 'setup', {TYPE => 'MEDIUMTEXT'}); + $dbh->bz_add_column('test_case_texts', 'breakdown', {TYPE => 'MEDIUMTEXT'}); + $dbh->bz_add_column('test_environments', 'product_id', {TYPE => 'INT2', NOTNULL => 1}, 0); + $dbh->bz_add_column('test_environments', 'isactive', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, 1); + $dbh->bz_add_column('test_plan_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0); + $dbh->bz_add_column('test_runs', 'default_tester_id', {TYPE => 'INT3'}); + $dbh->bz_add_column('test_runs', 'target_pass', {TYPE => 'INT1'}); + $dbh->bz_add_column('test_runs', 'target_completion', {TYPE => 'INT1'}); + $dbh->bz_add_column('test_run_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0); + $dbh->bz_add_column('test_builds', 'isactive', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, 1); + $dbh->bz_add_column('test_cases', 'estimated_time', {TYPE => 'TIME'}, 0); + $dbh->bz_add_column('test_case_runs', 'running_date', {TYPE => 'DATETIME'}, 0); + $dbh->bz_add_column('test_plan_types', 'description', {TYPE => 'MEDIUMTEXT'}, 0); + $dbh->bz_add_column('test_case_status', 'description', {TYPE => 'MEDIUMTEXT'}, 0); + $dbh->bz_add_column('test_case_run_status', 'description', {TYPE => 'MEDIUMTEXT'}, 0); + $dbh->bz_add_column('test_case_runs', 'iscurrent', {TYPE => 'INT1', NOTNULL => 1, DEFAULT => 0}, 0); + $dbh->bz_add_column('test_named_queries', 'type', {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0}, 0); + fixTables(); + + $dbh->bz_alter_column('test_attachment_data', 'attachment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_attachments', 'attachment_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_attachments', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1}); + $dbh->bz_alter_column('test_builds', 'build_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_activity', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_bugs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_bugs', 'case_run_id', {TYPE => 'INT4', UNSIGNED => 1}); + $dbh->bz_alter_column('test_case_components', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_dependencies', 'blocked', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_dependencies', 'dependson', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_plans', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_plans', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'build_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'case_run_status_id', {TYPE => 'INT2', NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'case_run_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_runs', 'iscurrent', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0'}); + $dbh->bz_alter_column('test_case_runs', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_run_status', 'case_run_status_id', {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_cases', 'case_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_cases', 'case_status_id', {TYPE => 'INT2', NOTNULL => 1}); + $dbh->bz_alter_column('test_case_status', 'case_status_id', {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_tags', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_texts', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_case_texts', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1}); + $dbh->bz_alter_column('test_environment_map', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_environment_map', 'property_id', {TYPE => 'INT4', UNSIGNED => 1}); + $dbh->bz_alter_column('test_environments', 'environment_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_named_queries', 'isvisible', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1}); + $dbh->bz_alter_column('test_plan_activity', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_plans', 'plan_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_plans', 'type_id', {TYPE => 'INT2', NOTNULL => 1}); + $dbh->bz_alter_column('test_plan_types', 'type_id', {TYPE => 'SMALLSERIAL', NOTNULL => 1, PRIMARYKEY => 1}, 0); + $dbh->bz_alter_column('test_plan_tags', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_plan_texts', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1}); + $dbh->bz_alter_column('test_plan_texts', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_plan_texts', 'plan_text', {TYPE => 'MEDIUMTEXT'}); + $dbh->bz_alter_column('test_run_activity', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_run_cc', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_runs', 'build_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_runs', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_runs', 'run_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_runs', 'start_date', {TYPE => 'DATETIME', NOTNULL => 1}); + $dbh->bz_alter_column('test_run_tags', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + + $dbh->bz_drop_index('test_attachments', 'AI_attachment_id'); + $dbh->bz_drop_index('test_attachments', 'attachment_id'); + $dbh->bz_drop_index('test_builds', 'build_id'); + $dbh->bz_drop_index('test_case_bugs', 'case_run_bug_id_idx'); + $dbh->bz_drop_index('test_case_bugs', 'case_run_id_idx'); + $dbh->bz_drop_index('test_case_categories', 'AI_category_id'); + $dbh->bz_drop_index('test_case_categories', 'category_name_idx'); + $dbh->bz_drop_index('test_case_categories', 'category_name_indx'); + $dbh->bz_drop_index('test_case_components', 'case_commponents_component_id_idx'); + $dbh->bz_drop_index('test_case_components', 'case_components_case_id_idx'); + $dbh->bz_drop_index('test_case_components', 'case_components_component_id_idx'); + $dbh->bz_drop_index('test_case_plans', 'case_plans_case_id_idx'); + $dbh->bz_drop_index('test_case_plans', 'case_plans_plan_id_idx'); + $dbh->bz_drop_index('test_case_runs', 'AI_case_run_id'); + $dbh->bz_drop_index('test_case_runs', 'case_run_build_idx'); + $dbh->bz_drop_index('test_case_runs', 'case_run_env_idx'); + $dbh->bz_drop_index('test_case_runs', 'case_run_id'); + $dbh->bz_drop_index('test_case_runs', 'case_run_id_2'); + $dbh->bz_drop_index('test_case_runs', 'case_run_run_id_idx'); + $dbh->bz_drop_index('test_case_runs', 'case_run_shortkey_idx'); + $dbh->bz_drop_index('test_case_runs', 'case_run_sortkey_idx'); + $dbh->bz_drop_index('test_case_run_status', 'AI_case_run_status_id'); + $dbh->bz_drop_index('test_case_run_status', 'case_run_status_name_idx'); + $dbh->bz_drop_index('test_case_run_status', 'case_run_status_sortkey_idx'); + $dbh->bz_drop_index('test_case_run_status', 'sortkey'); + $dbh->bz_drop_index('test_cases', 'AI_case_id'); + $dbh->bz_drop_index('test_cases', 'alias'); + $dbh->bz_drop_index('test_cases', 'case_id'); + $dbh->bz_drop_index('test_cases', 'case_id_2'); + $dbh->bz_drop_index('test_case_status', 'AI_case_status_id'); + $dbh->bz_drop_index('test_case_status', 'case_status_id'); + $dbh->bz_drop_index('test_case_status', 'test_case_status_name_idx'); + $dbh->bz_drop_index('test_cases', 'test_case_requirment_idx'); + $dbh->bz_drop_index('test_case_tags', 'case_tags_case_id_idx'); + $dbh->bz_drop_index('test_case_tags', 'case_tags_case_id_idx_v2'); + $dbh->bz_drop_index('test_case_tags', 'case_tags_tag_id_idx'); + $dbh->bz_drop_index('test_case_tags', 'case_tags_user_idx'); + $dbh->bz_drop_index('test_email_settings', 'test_event_user_event_dx'); + $dbh->bz_drop_index('test_email_settings', 'test_event_user_event_idx'); + $dbh->bz_drop_index('test_email_settings', 'test_event_user_relationship_idx'); + $dbh->bz_drop_index('test_environment_category', 'env_category_idx'); + $dbh->bz_drop_index('test_environment_element', 'env_element_category_idx'); + $dbh->bz_drop_index('test_environment_property', 'env_element_property_idx'); + $dbh->bz_drop_index('test_environments', 'environment_id'); + $dbh->bz_drop_index('test_environments', 'environment_name_idx'); + $dbh->bz_drop_index('test_fielddefs', 'AI_fieldid'); + $dbh->bz_drop_index('test_fielddefs', 'fielddefs_name_idx') if $dbh->isa('Bugzilla::DB::Mysql'); + $dbh->bz_drop_index('test_fielddefs', 'test_fielddefs_name_idx'); + $dbh->bz_drop_index('test_plans', 'AI_plan_id'); + $dbh->bz_drop_index('test_plans', 'plan_id'); + $dbh->bz_drop_index('test_plans', 'plan_id_2'); + $dbh->bz_drop_index('test_plan_tags', 'plan_tags_idx'); + $dbh->bz_drop_index('test_plan_tags', 'plan_tags_user_idx'); + $dbh->bz_drop_index('test_plan_types', 'AI_type_id'); + $dbh->bz_drop_index('test_plan_types', 'plan_type_name_idx'); + $dbh->bz_drop_index('test_run_cc', 'run_cc_run_id_who_idx'); + $dbh->bz_drop_index('test_runs', 'AI_run_id'); + $dbh->bz_drop_index('test_runs', 'run_id'); + $dbh->bz_drop_index('test_runs', 'run_id_2'); + $dbh->bz_drop_index('test_runs', 'test_run_plan_id_run_id__idx'); + $dbh->bz_drop_index('test_run_tags', 'run_tags_idx'); + $dbh->bz_drop_index('test_run_tags', 'run_tags_user_idx'); + $dbh->bz_drop_index('test_tags', 'AI_tag_id'); + $dbh->bz_drop_index('test_tags', 'tag_name'); + $dbh->bz_drop_index('test_tags', 'test_tag_name_idx'); + $dbh->bz_drop_index('test_tags', 'test_tag_name_indx'); + $dbh->bz_drop_index('test_runs', 'test_runs_summary_idx'); + + $dbh->bz_add_index('test_attachment_data', 'test_attachment_data_primary_idx', ['attachment_id']); + $dbh->bz_add_index('test_attachments', 'test_attachments_submitter_idx', ['submitter_id']); + $dbh->bz_add_index('test_builds', 'build_milestone_idx', ['milestone']); + $dbh->bz_add_index('test_builds', 'build_name_idx', ['name']); + $dbh->bz_add_index('test_builds', 'build_prod_idx', {FIELDS => [qw(build_id product_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_builds', 'build_product_id_name_idx', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_attachments', 'test_case_attachments_primary_idx', ['attachment_id']); + $dbh->bz_add_index('test_case_bugs', 'case_bugs_bug_id_idx', ['bug_id']); + $dbh->bz_add_index('test_case_bugs', 'case_bugs_case_id_idx', ['case_id']); + $dbh->bz_add_index('test_case_bugs', 'case_bugs_case_run_id_idx', ['case_run_id']); + $dbh->bz_add_index('test_case_categories', 'category_name_idx_v2', ['name']); + $dbh->bz_add_index('test_case_categories', 'category_product_id_name_idx', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_categories', 'category_product_idx', {FIELDS => [qw(category_id product_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_components', 'components_case_id_idx', {FIELDS => [qw(case_id component_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_components', 'components_component_id_idx', ['component_id']); + $dbh->bz_add_index('test_case_dependencies', 'case_dependencies_blocked_idx', ['blocked']); + $dbh->bz_add_index('test_case_dependencies', 'case_dependencies_primary_idx', {FIELDS => [qw(dependson blocked)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_plans', 'test_case_plans_case_idx', [qw(case_id)]); + $dbh->bz_add_index('test_case_plans', 'test_case_plans_primary_idx', {FIELDS => [qw(plan_id case_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_runs', 'case_run_build_env_idx', {FIELDS => [qw(run_id case_id build_id environment_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_runs', 'case_run_build_idx_v2', ['build_id']); + $dbh->bz_add_index('test_case_runs', 'case_run_env_idx_v2', ['environment_id']); + $dbh->bz_add_index('test_case_runs', 'case_run_status_idx', ['case_run_status_id']); + $dbh->bz_add_index('test_case_runs', 'case_run_text_ver_idx', ['case_text_version']); + $dbh->bz_add_index('test_cases', 'test_case_requirement_idx', ['requirement']); + $dbh->bz_add_index('test_cases', 'test_case_status_idx', ['case_status_id']); + $dbh->bz_add_index('test_cases', 'test_case_tester_idx', ['default_tester_id']); + $dbh->bz_add_index('test_case_tags', 'case_tags_case_id_idx_v3', [qw(case_id)]); + $dbh->bz_add_index('test_case_tags', 'case_tags_primary_idx', {FIELDS => [qw(tag_id case_id userid)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_tags', 'case_tags_secondary_idx', {FIELDS => [qw(tag_id case_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_case_tags', 'case_tags_userid_idx', [qw(userid)]); + $dbh->bz_add_index('test_email_settings', 'test_email_setting_user_id_idx', {FIELDS => [qw(userid relationship_id eventid)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_category', 'test_environment_category_key1', {FIELDS => [qw(env_category_id product_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_category', 'test_environment_category_key2', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_element', 'test_environment_element_key1', {FIELDS => [qw(element_id env_category_id)], TYPE => 'UNIQUE'},); + $dbh->bz_add_index('test_environment_element', 'test_environment_element_key2', {FIELDS => [qw(env_category_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_map', 'test_environment_map_key3', {FIELDS => [qw(environment_id element_id property_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_property', 'test_environment_property_key1', {FIELDS => [qw(property_id element_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environment_property', 'test_environment_property_key2', {FIELDS => [qw(element_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environments', 'environment_name_idx_v2', ['name']); + $dbh->bz_add_index('test_environments', 'test_environments_key1', {FIELDS => [qw(environment_id product_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_environments', 'test_environments_key2', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_named_queries', 'test_namedquery_primary_idx', {FIELDS => [qw(userid name)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_plan_activity', 'plan_activity_changed_idx', ['changed']); + $dbh->bz_add_index('test_plan_activity', 'plan_activity_field_idx', ['fieldid']); + $dbh->bz_add_index('test_plan_activity', 'plan_activity_primary_idx', ['plan_id']); + $dbh->bz_add_index('test_plan_attachments', 'test_plan_attachments_primary_idx', ['attachment_id']); + $dbh->bz_add_index('test_plan_permissions', 'testers_plan_grant_idx', ['grant_type']); + $dbh->bz_add_index('test_plan_tags', 'plan_tags_plan_id_idx', [qw(plan_id)]); + $dbh->bz_add_index('test_plan_tags', 'plan_tags_primary_idx', {FIELDS => [qw(tag_id plan_id userid)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_plan_tags', 'plan_tags_secondary_idx', {FIELDS => [qw(tag_id plan_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_plan_tags', 'plan_tags_userid_idx', [qw(userid)]); + $dbh->bz_add_index('test_run_activity', 'run_activity_field_idx', ['fieldid']); + $dbh->bz_add_index('test_run_cc', 'test_run_cc_primary_idx', {FIELDS => [qw(run_id who)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_run_cc', 'test_run_cc_who_idx', [qw(who)]); + $dbh->bz_add_index('test_runs', 'test_run_build_idx', ['build_id']); + $dbh->bz_add_index('test_runs', 'test_run_env_idx', ['environment_id']); + $dbh->bz_add_index('test_runs', 'test_run_plan_id_run_id_idx', [qw(plan_id run_id)]); + $dbh->bz_add_index('test_runs', 'test_run_plan_ver_idx', ['plan_text_version']); + $dbh->bz_add_index('test_runs', 'test_run_tester_idx', ['default_tester_id']); + $dbh->bz_add_index('test_run_tags', 'run_tags_primary_idx', {FIELDS => [qw(tag_id run_id userid)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_run_tags', 'run_tags_run_id_idx', [qw(run_id)]); + $dbh->bz_add_index('test_run_tags', 'run_tags_secondary_idx', {FIELDS => [qw(tag_id run_id)], TYPE => 'UNIQUE'}); + $dbh->bz_add_index('test_run_tags', 'run_tags_userid_idx', [qw(userid)]); + $dbh->bz_add_index('test_tags', 'test_tag_name_idx_v2', [qw(tag_name)]); + + populateMiscTables(); + populateEnvTables(); + migrateEnvData(); +} + +sub updateACLs { + my $dbh = Bugzilla->dbh; + return unless $dbh->selectrow_array("SELECT COUNT(*) FROM test_plan_permissions") == 0; + + print "Populating test plan ACLs ...\n"; + my $ref = $dbh->selectall_arrayref("SELECT plan_id, author_id FROM test_plans", {'Slice' =>{}}); + foreach my $plan (@$ref){ + my ($finished) = $dbh->selectrow_array( + "SELECT COUNT(*) FROM test_plan_permissions + WHERE plan_id = ? AND userid = ?", + undef, ($plan->{'plan_id'}, $plan->{'author_id'})); + next if ($finished); + $dbh->do("INSERT INTO test_plan_permissions(userid, plan_id, permissions) + VALUES(?,?,?)", + undef, ($plan->{'author_id'}, $plan->{'plan_id'}, 15)); + } +} + +sub migrateAttachments { + my $dbh = Bugzilla->dbh; + return unless $dbh->bz_column_info('test_attachments', 'case_id'); + print "Migrating attachments...\n"; + + my $rows = $dbh->selectall_arrayref( + "SELECT attachment_id, case_id, plan_id + FROM test_attachments", {'Slice' => {}}); + + foreach my $row (@$rows){ + if ($row->{'case_id'}){ + $dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id) + VALUES (?,?)", undef, ($row->{'attachment_id'}, $row->{'case_id'})); + } + elsif ($row->{'plan_id'}){ + $dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id) + VALUES (?,?)", undef, ($row->{'attachment_id'}, $row->{'plan_id'})); + } + } + $dbh->bz_drop_column('test_attachments', 'case_id'); + $dbh->bz_drop_column('test_attachments', 'plan_id'); +} + +sub populateMiscTables { + my $dbh = Bugzilla->dbh; + + # Fix and add values to an existing intall. + + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('ERROR', 7)") + if $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status") + && ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status WHERE name = ?", undef, 'ERROR'); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('target_pass', 'Target Pass Rate', 'test_runs')") + if $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs") + && ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs WHERE name = ?", undef, 'target_pass'); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('target_completion', 'Target Completion Rate', 'test_runs')") + if $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs") + && ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs WHERE name = ?", undef, 'target_completion'); + $dbh->do("UPDATE test_environment_map SET property_id = ? where property_id = ?",undef, undef, 0); + + if ($dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status")){ + $dbh->do("UPDATE test_case_run_status SET name='IDLE', sortkey=1 where case_run_status_id=1"); + $dbh->do("UPDATE test_case_run_status SET name='PASSED', sortkey=4 where case_run_status_id=2"); + $dbh->do("UPDATE test_case_run_status SET name='FAILED', sortkey=5 where case_run_status_id=3"); + $dbh->do("UPDATE test_case_run_status SET name='RUNNING', sortkey=2 where case_run_status_id=4"); + $dbh->do("UPDATE test_case_run_status SET name='PAUSED', sortkey=3 where case_run_status_id=5"); + $dbh->do("UPDATE test_case_run_status SET name='BLOCKED', sortkey=6 where case_run_status_id=6"); + $dbh->do("UPDATE test_case_run_status SET name='ERROR', sortkey=7 where case_run_status_id=7"); + } + + # Insert initial values in static tables. Going out on a limb and + # assuming that if one table is empty, they all are. + return if $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_status"); + + print "Populating test_case_run_status table ...\n"; + print "Populating test_case_status table ...\n"; + print "Populating test_plan_types table ...\n"; + print "Populating test_fielddefs table ...\n"; + + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('IDLE', 1)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('PASSED', 4)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('FAILED', 5)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('RUNNING', 2)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('PAUSED', 3)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('BLOCKED', 6)"); + $dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('ERROR', 7)"); + $dbh->do("INSERT INTO test_case_status (name) VALUES ('PROPOSED')"); + $dbh->do("INSERT INTO test_case_status (name) VALUES ('CONFIRMED')"); + $dbh->do("INSERT INTO test_case_status (name) VALUES ('DISABLED')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Unit')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Integration')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Function')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('System')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Acceptance')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Installation')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Performance')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Product')"); + $dbh->do("INSERT INTO test_plan_types (name) VALUES ('Interoperability')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('isactive', 'Archived', 'test_plans')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('name', 'Plan Name', 'test_plans')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('type_id', 'Plan Type', 'test_plans')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('case_status_id', 'Case Status', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('category_id', 'Category', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('priority_id', 'Priority', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('summary', 'Run Summary', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('isautomated', 'Automated', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('alias', 'Alias', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('requirement', 'Requirement', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('script', 'Script', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('arguments', 'Argument', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('product_id', 'Product', 'test_plans')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('default_product_version', 'Default Product Version', 'test_plans')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('environment_id', 'Environment', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('product_version', 'Product Version', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('build_id', 'Default Build', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('plan_text_version', 'Plan Text Version', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('manager_id', 'Manager', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('default_tester_id', 'Default Tester', 'test_cases')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('stop_date', 'Stop Date', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('summary', 'Run Summary', 'test_runs')"); + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('notes', 'Notes', 'test_runs')"); +} + +sub populateEnvTables { + my $dbh = Bugzilla->dbh; + + my $sth; + my $ary_ref; + my $value; + + return unless $dbh->selectrow_array("SELECT COUNT(*) FROM test_environment_category") == 0; + if ($dbh->selectrow_array("SELECT COUNT(*) FROM test_environment_element") != 0) { + print STDERR "\npopulateEnv: Fatal Error: test_environment_category " . + "is empty but\ntest_environment_element is not. This ought " . + "to be impossible.\n\n"; + return; + } + + $dbh->bz_start_transaction(); + + print "Populating test_environment_category table ...\n"; + $dbh->do("INSERT INTO test_environment_category (product_id, name) " . + "VALUES (0, 'Operating System')"); + $dbh->do("INSERT INTO test_environment_category (product_id, name) " . + "VALUES (0, 'Hardware')"); + + print "Populating test_environment_element table ...\n"; + $sth = $dbh->prepare("INSERT INTO test_environment_element " . + "(env_category_id, name, parent_id, isprivate) " . + "VALUES (?, ?, ?, ?)"); + $ary_ref = $dbh->selectcol_arrayref("SELECT value FROM op_sys"); + foreach $value (@$ary_ref) { + $sth->execute(1, $value, 0, 0); + } + $ary_ref = $dbh->selectcol_arrayref("SELECT value FROM rep_platform"); + foreach $value (@$ary_ref) { + $sth->execute(2, $value, 0, 0); + } + + $dbh->bz_commit_transaction(); +} + +sub migrateEnvData { + my $dbh = Bugzilla->dbh; + my $sth; + my $value; + my $os_mapping; + my $platform_mapping; + my $ary_ref; + my $i; + + return unless $dbh->bz_column_info('test_environments', 'op_sys_id'); + + # Map between IDs in op_sys table and IDs in + # test_environment_element table. + $os_mapping = $dbh->selectall_hashref("SELECT " . + "os.id AS op_sys_id, " . + "env_elem.element_id AS element_id " . + "FROM op_sys os, test_environment_element env_elem " . + "WHERE os.value = env_elem.name " . + "AND env_elem.env_category_id = 1", + 'op_sys_id'); + + # Map between IDs in rep_platform table and IDs in + # test_environment_element table. + $platform_mapping = $dbh->selectall_hashref("SELECT " . + "platform.id AS rep_platform_id, " . + "env_elem.element_id AS element_id " . + "FROM rep_platform platform, test_environment_element env_elem " . + "WHERE platform.value = env_elem.name " . + "AND env_elem.env_category_id = 2", + 'rep_platform_id'); + + $dbh->bz_start_transaction(); + print "Migrating data from test_environments to test_environment_map ...\n"; + $sth = $dbh->prepare("INSERT INTO test_environment_map " . + "(environment_id, property_id, element_id, value_selected) " . + "VALUES (?, ?, ?, ?)"); + $ary_ref = $dbh->selectall_arrayref("SELECT environment_id, op_sys_id " . + "FROM test_environments"); + foreach $i (@$ary_ref) { + $sth->execute(@$i[0], 0, $os_mapping->{@$i[1]}->{'element_id'}, ''); + } + $ary_ref = $dbh->selectall_arrayref("SELECT environment_id, rep_platform_id " . + "FROM test_environments"); + foreach $i (@$ary_ref) { + $sth->execute(@$i[0], 0, $platform_mapping->{@$i[1]}->{'element_id'}, ''); + } + $dbh->bz_commit_transaction(); + + print "Saving data from test_environments.xml column into text files ...\n"; + $ary_ref = $dbh->selectall_arrayref("SELECT environment_id, name, xml " . + "FROM test_environments WHERE xml != ''"); + foreach $value (@$ary_ref) { + open(FH, ">environment_" . @$value[0] . "_xml.txt"); + print FH "environment ID: @$value[0]\n"; + print FH "environment name: @$value[1]\n"; + print FH "environment xml:\n@$value[2]\n"; + close(FH); + } + + $dbh->bz_drop_column('test_environments', 'op_sys_id'); + $dbh->bz_drop_column('test_environments', 'rep_platform_id'); + $dbh->bz_drop_column('test_environments', 'xml'); +} + +sub fixTables { + my $dbh = Bugzilla->dbh; + + # Fix test_case_bugs table so that all case_id fields are not null. + my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_bugs WHERE case_id IS NULL"); + if ($count){ + require Bugzilla::Testopia::TestCaseRun; + my $caseruns = $dbh->selectcol_arrayref("SELECT case_run_id FROM test_case_bugs WHERE case_id IS NULL"); + my $sth = $dbh->prepare_cached("UPDATE test_case_bugs SET case_id = ? WHERE case_run_id = ?"); + foreach my $cr (@$caseruns){ + my $caserun = Bugzilla::Testopia::TestCaseRun->new($cr); + $sth->execute($caserun->case->id, $cr); + } + } + + # If we can't add a unique index to (case_id,component_id), then we + # need to remove duplicate rows from test_case_components. + eval{ + $dbh->bz_add_index('test_case_components', 'components_case_id_idx', {FIELDS => [qw(case_id component_id)], TYPE => 'UNIQUE'}); + }; + if ($@){ + print "Running component fix...\n"; + my $rows = $dbh->selectall_arrayref("SELECT * FROM test_case_components", {"Slice" => {}}); + my $seen; + foreach my $row (@$rows){ + my $line = $row->{'case_id'} . "-" . $row->{'component_id'}; + if (!$seen->{$line}){ + $seen->{$line} = 'seen'; + } + elsif ($seen->{$line} eq 'seen'){ + $dbh->do("DELETE FROM test_case_components + WHERE case_id = ? AND component_id = ?", + undef, ($row->{'case_id'}, $row->{'component_id'})); + $dbh->do("INSERT INTO test_case_components + VALUES(?,?)", + undef, ($row->{'case_id'}, $row->{'component_id'})); + $seen->{$line} = 'fixed'; + } + elsif ($seen->{$line} eq 'fixed'){ + next; + } + } + } +} + +sub createGroup { + Bugzilla::Group->create({ + name => 'Testers', + description => 'Can read and write all test plans, runs, and cases.', + isbuggroup => 0 }) unless new Bugzilla::Group({name => 'Testers'}); +} + +# A spot for fixing stuff at the very end. +sub finalFixups { + my $dbh = Bugzilla->dbh; + + # We added the estimated_time field later, so we can't add it + # inside populateMiscTables(). + unless ($dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs " . + "WHERE name = 'estimated_time'")) { + $dbh->do("INSERT INTO test_fielddefs (name, description, table_name) " . + "VALUES ('estimated_time', 'Estimated Time', 'test_cases')"); + } +} diff --git a/extensions/testopia/code/post_bug-after_creation.pl b/extensions/testopia/code/post_bug-after_creation.pl new file mode 100644 index 0000000..372ed92 --- /dev/null +++ b/extensions/testopia/code/post_bug-after_creation.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +use strict; +use Bugzilla::Testopia::TestCase; + +my $vars = Bugzilla->hook_args->{vars}; +my $cgi = Bugzilla->cgi; + +my $caserun_id = $cgi->param('caserun_id'); +my $case_id = $cgi->param('case_id'); +if (detaint_natural($caserun_id)) { + my $caserun = Bugzilla::Testopia::TestCaseRun->new($cgi->param('caserun_id')); + ThrowUserError("invalid-test-id-non-existent", {'id' => $caserun_id, 'type' => 'Case-Run'}) unless $caserun; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + + $caserun->attach_bug($vars->{'id'}); + + $vars->{'caserun'} = $caserun; +} +elsif (detaint_natural($case_id)) { + my $case = Bugzilla::Testopia::TestCase->new($cgi->param('case_id')); + ThrowUserError("invalid-test-id-non-existent", {'id' => $case_id, 'type' => 'Case'}) unless $case; + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + + $case->attach_bug($vars->{'id'}); + + $vars->{'case'} = $case; +} + diff --git a/extensions/testopia/code/product-confirm_delete.pl b/extensions/testopia/code/product-confirm_delete.pl new file mode 100644 index 0000000..07a02a0 --- /dev/null +++ b/extensions/testopia/code/product-confirm_delete.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use Bugzilla::Testopia::Product; + +my $vars = Bugzilla->hook_args->{vars}; + +$vars->{'testopia_product'} = new Bugzilla::Testopia::Product($vars->{product}->id); \ No newline at end of file diff --git a/skins/standard/testopia.css b/skins/standard/testopia.css new file mode 100644 index 0000000..34af404 --- /dev/null +++ b/skins/standard/testopia.css @@ -0,0 +1,266 @@ + +#header .label { + display: none; +} + +#header li.tr_find { +} +#header li.tr_saved_search { +} + +#header .testopia_btn { + background-color:#546292; + color:#ffffff; + font-weight:bold; + font-size:100%; + border:none; +} + +#footer li.tr_find { +} + +#footer li.tr_saved_search { +} +#footer .tr_btn { + background-color: #808285; + font-size: 80%; + color:#ffffff; + font-size:100%; + border:none; +} + +/* old default.css */ +textarea { + /* background-color: #fff; */ + border: 1px solid #999; +} + +.resultsTable { + border-collapse: collapse; /*cellspacing*/ + border-spacing: 0px; +} +.resultsTable td { /*only immediate tds of mytable*/ + padding: 4px; /*cellpadding*/ +} + +.resultsTable td a:hover { + text-decoration:underline overline; +} + +.resultsTable th { + border-color:#FFF; + background-color:#C0FFC0; + + border-style:solid; + border-width: 2px; + padding: 4px; +} + +.dlgTable { + border-collapse: collapse; /*cellspacing*/ + border-spacing: 0px; +} + +.dlgTable td { /*only immediate tds of mytable*/ + padding: 4px; /*cellpadding*/ + background-color:#EEE; +} + +.spacerTable { + border-collapse: collapse; /*cellspacing*/ + border-spacing: 0px; +} +.spacerTable td { /*only immediate tds of mytable*/ + padding: 16px; /*cellpadding*/ + background-color:#FFF; +} + +.menuTable { + border-collapse: collapse; /*cellspacing*/ + border-spacing: 0px; +} +.menuTable td { /*only immediate tds of mytable*/ + padding: 4px; /*cellpadding*/ + background-color:#EEE; + + border-style:solid; + border-width: 3px; + border-color:#FFF; +} + +.evenRow_first { + background-color:#FFF; +} +.evenRow { + background-color:#FFF; +} +.evenRow td{ + background-color:#FFF;border-top-style:solid;border-top-width:1px;border-top-color:#CCC; +} + +.oddRow_first { + background-color:#EEE; +} +.oddRow { + background-color:#EEE; +} +.oddRow td { + background-color:#EEE;border-top-style:solid;border-top-width:1px;border-top-color:#CCC; +} + +.highlightedCell { + border-width:1px; + border-style:solid; + background-color:lightyellow; +} + +/* tr_showcaselog */ + +.short_body { + border-bottom:1px solid #000; + border-left:1px solid #000; + border-right:1px solid #000; +} + +.short_head { + border-top:1px solid #000; + border-left:1px solid #000; + border-right:1px solid #000; +} + +.ae_dv { + display:none; + margin-left:15px; + border-width:0px; +} + +.ae_tb { + width:100%; + border:0; + margin:0; + margin-right:30px; + margin-bottom:10px; + border-collapse:collapse; + border-spacing: 0px; +} + +.ae_s { + border-style:solid; + border-width:1px; + border-color:#000; + background-color:#FFFFE0; +} + +.cc_i { + float:left; + margin-left:10px; +} + +.cc_xx { + clear:both; + margin-right:30px; + padding-top:5px; +} + +.cc_trg { + padding-left:5px; + padding-right:5px; + vertical-align:baseline; +} + +/* TODO: refactor these two: */ + +#floatMsg { + text-align:right; + background-color:#FFDD66; + color:black; + padding:4px; + padding-left:20px; + padding-right:20px; + font-family:Arial; + font-weight:bold; + font-size:12px; + display:none; +} + +.floatMsg { + text-align:right; + background-color:#FFDD66; + color:black; + padding:4px; + padding-left:20px; + padding-right:20px; + font-family:Arial; + font-weight:bold; + font-size:12px; + display:none; +} + +.tr_button { + width:100px; +} + +a img { + border: none; +} + +h3 { + border-bottom: 1px dotted #000; +} +dt { + font-weight: bold; + font-size: medium; +} + +.tr_row_odd +{ + background-color: #F7F7F7; + color: #000000; +} + +.tr_row_even +{ + background-color: #FFFFFF; + color: #000000; +} + +.bz_row_header { background-color: #E7E9E7; + font-weight: bolder; + } +.bz_row_data { padding-bottom: 5px;} + +.img_button_16x { + background-repeat: no-repeat; +} + +.plan_archived { + background: url(../../testopia/img/archived.png); + background-repeat: no-repeat; + width: 120px; + height: 32px; +} + +.x-progress-bar-red{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/red_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;} +.x-progress-bar-green{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/green_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;} +.x-progress-bar-orange{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/orange_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;} +.x-progress-text-main{font-size:11px;font-weight:bold;color:#fff;padding:3px 5px;overflow:hidden;position:absolute;left:0;text-align:center;} +.x-progress-text-back-main{color:#000;line-height:16px;font-weight:bold;} + +.x-hidden {position: absolute; left: -1000px; top: -100px;} + +.t1 { background-color: #ffffff } /* white */ +.t2 { background-color: #dfefff } /* light blue */ +.t3 { background-color: #dddddd } /* grey */ +.t4 { background-color: #c3d3ed } /* darker blue */ +.ttotal { background-color: #cfffdf } /* light green */ + +.msg .x-box-mc { + font-size:14px; +} +#msg-div { + position:absolute; + left:35%; + top:10px; + width:250px; + z-index:20000; +} diff --git a/template/en/default/admin/params/testopia.html.tmpl b/template/en/default/admin/params/testopia.html.tmpl new file mode 100644 index 0000000..ec62c2e --- /dev/null +++ b/template/en/default/admin/params/testopia.html.tmpl @@ -0,0 +1,85 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Test Runner System. + # + # The Initial Developer of the Original Code is Maciej Maczynski. + # Portions created by Maciej Maczynski are Copyright (C) 2001 + # Maciej Maczynski. All Rights Reserved. + # + # Contributor(s): Ed Fuentetaja + # Greg Hendricks + #%] + +[% + title = "Testopia" + desc = "Set Testopia parameters" +%] + +[% param_descs = { + "private-cases-log" => "If this option is on, the tester cannot view other testers' cases", + + "allow-test-deletion" => "If this option is on, users can delete objects including plans and cases", + + "testopia-allow-group-member-deletes" => "If this option is on, members of the Testers group will be + allowed to delete test objects", + + "testopia-default-plan-testers-regexp" => "This is the default regular expression for granting + access to new test plans", + + "print-tag-in-case-log" => 'If this option is on, the entire tag text is printed in a test case ' _ + 'log entry. Otherwise, only an href to the tag is put there.', + + "new-case-action-template" => "This is the text to be put in a newly created test case \"action\" field", + + "new-case-results-template" => "This is the text to be put in a newly created test case \"expected results\" field", + + "bug-to-test-case-summary" => 'This is the default summary text used when generating a test case ' _ + 'out of a bug. The special symbol %id% is replaced with the bug id and ' _ + "%summary% is replaced with the bug's summary", + + "bug-to-test-case-action" => 'This is the default action text used when generating a test case ' _ + 'out of a bug. The special symbol %id% is replaced with a ' _ + "hyperlink to the bug's page. The special symbol \%description\% is " _ + "replaced with the bug's description", + + "bug-to-test-case-results" => 'This is the default results text used when generating a test case ' _ + 'out of a bug. The special symbol %id% is replaced with a ' _ + "hyperlink to the bug's page", + + "default-test-case-status" => 'Default status for newly created test cases.', + + "new-testrun-email-notif" => 'E-mail message sent to assigned testers when a new test run is started. ' _ + 'There are some special symbols replaced at run time:
' _ + '%to%: list of assigned testers email addresses
' _ + '%summary%: test run summary
' _ + '%plan%: plan\'s name
' _ + '%plan_id%: plan\'s id
' _ + '%product%: product\'s name
' _ + '%product_id%: product\'s id', + + "case-failed-email-notif" => 'E-mail message sent when a test case log is marked as \'failed\'. ' _ + 'There are some special symbols replaced at run time:
' _ + '%id%: test case log id
' _ + '%manager%: test run\'s manager
' _ + '%test_run%: test run\'s summary
' _ + '%tester%: tester
' _ + '%component%: component\'s name', + + "tester-completed-email-notif" => 'E-mail message sent when a tester has run every assigned test case.', + + "test-run-completed-email-notif" => 'E-mail message sent when every test case in a test run is completed.', + "testopia-max-allowed-plan-testers" => 'Limit to how many users a plan access regular expression should match', + "testopia-debug" => 'Developer: Load individual files (Slowest)
ON: Use the debug version of Javascript files (Slower but needful if reporting javascript related problems)
OFF: Use the compressed version (faster)', + + } + +%] \ No newline at end of file diff --git a/template/en/default/hook/admin/products/confirm-delete.html.tmpl/confirmation/testopia.html.tmpl b/template/en/default/hook/admin/products/confirm-delete.html.tmpl/confirmation/testopia.html.tmpl new file mode 100644 index 0000000..f814376 --- /dev/null +++ b/template/en/default/hook/admin/products/confirm-delete.html.tmpl/confirmation/testopia.html.tmpl @@ -0,0 +1,9 @@ + + + + +
+ This product has [% testopia_product.plans.size %] test plans + and [% testopia_product.runs.size %] test runs. + Deleting this product will delete all associated Testopia data including + plans, cases, runs, builds, and environments.
\ No newline at end of file diff --git a/template/en/default/hook/bug/create/create.html.tmpl/end/tr.html.tmpl b/template/en/default/hook/bug/create/create.html.tmpl/end/tr.html.tmpl new file mode 100644 index 0000000..196270c --- /dev/null +++ b/template/en/default/hook/bug/create/create.html.tmpl/end/tr.html.tmpl @@ -0,0 +1,5 @@ +[%# 1.0@bugzilla.org %] + + [% IF caserun_id %] + Back to test Case. + [% END %] diff --git a/template/en/default/hook/bug/create/create.html.tmpl/form/tr.html.tmpl b/template/en/default/hook/bug/create/create.html.tmpl/form/tr.html.tmpl new file mode 100644 index 0000000..871d840 --- /dev/null +++ b/template/en/default/hook/bug/create/create.html.tmpl/form/tr.html.tmpl @@ -0,0 +1,8 @@ +[%# 1.0@bugzilla.org %] + + [% IF caserun_id %] + + [% END %] + [% IF case_id %] + + [% END %] diff --git a/template/en/default/hook/bug/create/created.html.tmpl/message/tr.html.tmpl b/template/en/default/hook/bug/create/created.html.tmpl/message/tr.html.tmpl new file mode 100644 index 0000000..143dee1 --- /dev/null +++ b/template/en/default/hook/bug/create/created.html.tmpl/message/tr.html.tmpl @@ -0,0 +1,5 @@ +[%# 1.0@bugzilla.org %] + +[% IF caserun %] +

[% terms.Bug %] attached to Test case [% case.id FILTER none %]

+[% END %] diff --git a/template/en/default/hook/bug/edit.html.tmpl/after_custom_fields/tr.html.tmpl b/template/en/default/hook/bug/edit.html.tmpl/after_custom_fields/tr.html.tmpl new file mode 100644 index 0000000..ecc096a --- /dev/null +++ b/template/en/default/hook/bug/edit.html.tmpl/after_custom_fields/tr.html.tmpl @@ -0,0 +1,16 @@ +[%# 1.0@bugzilla.org %] + +[% test_case_count = bug.get_test_case_count %] + diff --git a/template/en/default/hook/bug/process/results.html.tmpl/links/tr.html.tmpl b/template/en/default/hook/bug/process/results.html.tmpl/links/tr.html.tmpl new file mode 100644 index 0000000..6ecb226 --- /dev/null +++ b/template/en/default/hook/bug/process/results.html.tmpl/links/tr.html.tmpl @@ -0,0 +1,6 @@ +[%# 1.0@bugzilla.org %] + +[% IF case_id %] +
+ Back to test case. +[% END %] diff --git a/template/en/default/hook/global/banner.html.tmpl/version/testopia.html.tmpl b/template/en/default/hook/global/banner.html.tmpl/version/testopia.html.tmpl new file mode 100644 index 0000000..ffa4a3c --- /dev/null +++ b/template/en/default/hook/global/banner.html.tmpl/version/testopia.html.tmpl @@ -0,0 +1,21 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Testopia System. + # + # The Initial Developer of the Original Code is Greg Hendricks. + # Portions created by Greg Hendricks are Copyright (C) 2006 + # Novell. All Rights Reserved. + # + # Contributor(s): Greg Hendricks + #%] + Testopia + Version 2.1 diff --git a/template/en/default/hook/global/code-error.html.tmpl/errors/testopia_errors.html.tmpl b/template/en/default/hook/global/code-error.html.tmpl/errors/testopia_errors.html.tmpl new file mode 100644 index 0000000..67253dd --- /dev/null +++ b/template/en/default/hook/global/code-error.html.tmpl/errors/testopia_errors.html.tmpl @@ -0,0 +1,28 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Test Runner System. + # + # The Initial Developer of the Original Code is Maciej Maczynski. + # Portions created by Maciej Maczynski are Copyright (C) 2001 + # Maciej Maczynski. All Rights Reserved. + # + # Contributor(s): Greg Hendricks + #%] + + [% IF error == "testopia_undefined_field" %] + [% title = "Missing Field in test_fielddefs" %] + The [% fieldname %] field was not found for the [% table %] table while logging activity. + [% ELSIF error == "testopia-missing-attachment-key" %] + [% title = "Missing Key" %] + You have requested to save an attachment, but I didn't see which test plan + or test case you had in mind to store it with. + [% END %] diff --git a/template/en/default/hook/global/common-links.html.tmpl/link-row/testopia.html.tmpl b/template/en/default/hook/global/common-links.html.tmpl/link-row/testopia.html.tmpl new file mode 100644 index 0000000..19d928b --- /dev/null +++ b/template/en/default/hook/global/common-links.html.tmpl/link-row/testopia.html.tmpl @@ -0,0 +1,60 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Testopia System. + # + # The Initial Developer of the Original Code is Greg Hendricks. + # Portions created by Greg Hendricks are Copyright (C) 2006 + # Novell. All Rights Reserved. + # + # Contributor(s): Greg Hendricks + #%] + +[% IF user.login AND user.settings.view_testopia.value == 'on' %] + +
Testopia:
+ +[% END %] + diff --git a/template/en/default/hook/global/header.html.tmpl/additional_header/testopia_styles.html.tmpl b/template/en/default/hook/global/header.html.tmpl/additional_header/testopia_styles.html.tmpl new file mode 100644 index 0000000..1935318 --- /dev/null +++ b/template/en/default/hook/global/header.html.tmpl/additional_header/testopia_styles.html.tmpl @@ -0,0 +1,4 @@ + + diff --git a/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl b/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl new file mode 100644 index 0000000..60df3c2 --- /dev/null +++ b/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl @@ -0,0 +1,36 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Testopia System. + # + # The Initial Developer of the Original Code is Greg Hendricks. + # Portions created by Greg Hendricks are Copyright (C) 2001 + # Greg Hendricks. All Rights Reserved. + # + # Contributor(s): Greg Hendricks + #%] +[% IF user.login AND user.settings.view_testopia.value == 'on' %] + +"].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E){return E}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:"Test cases "+H.result.tclist.join(",")+' Copied successfully View as List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[{xtype:"textfield",id:"caserun_append_note_fld",width:1000}],buttons:[{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Test Run Copied",msg:M+"Test runs "+K.result.runlist.join(",")+' Copied successfully. View as List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file diff --git a/testopia/testrunner.pl b/testopia/testrunner.pl new file mode 100644 index 0000000..20d3235 --- /dev/null +++ b/testopia/testrunner.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Jeff Dayley +use strict; + +use lib 't'; +use lib '.'; +use lib '../..'; + +use Test::Unit::Debug qw(debug_pkgs); +use Test::Unit::TestRunner; + +use TAP::Harness; + +use Testopia::Test::Constants; + +my @api_tests = qw( + API_Build.pm + API_Environment.pm + API_Product.pm + API_TestCase.pm + API_TestCaseRun.pm + API_TestPlan.pm + API_TestRun.pm +); + +if ( $ARGV[0] && $ARGV[0] =~ /^API/ ) { + my $testrunner; + use Test::More; + + # Uncomment and edit to debug individual packages. + #debug_pkgs(qw/Test::Unit::TestCase/); + foreach my $test (@api_tests) { + $testrunner = Test::Unit::TestRunner->new(); + $testrunner->start($test); + } +} +else { + my %args; + my $harness; + my $login_types = LOGIN_CREDENTIALS; + + my @tests = qw( + t/SE_environments.t + ); + + my $type = 'anonymous'; + + #foreach my $type (keys %$login_types){ + %args = ( + verbosity => 1, + lib => [ '.', '..', 't' ], + test_args => + [ $login_types->{$type}, $login_types->{$type}->{'login_name'}, $login_types->{$type}->{'password'} ], + ); + + $harness = TAP::Harness->new( \%args ); + $harness->runtests(@tests); + + #} +} diff --git a/testopia/tools/readme b/testopia/tools/readme new file mode 100644 index 0000000..08b6c01 --- /dev/null +++ b/testopia/tools/readme @@ -0,0 +1,3 @@ +If you are installing Testopia on Windows, you will need to +install patch.exe into this directory. It is available here: + http://gnuwin32.sourceforge.net/packages/patch.htm diff --git a/tr_admin.cgi b/tr_admin.cgi new file mode 100755 index 0000000..f279fba --- /dev/null +++ b/tr_admin.cgi @@ -0,0 +1,104 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; + +local our $template = Bugzilla->template; +my $dbh = Bugzilla->dbh; +my $cgi = Bugzilla->cgi; + +use vars qw($vars); + +Bugzilla->login(LOGIN_REQUIRED); + +print $cgi->header; +ThrowUserError("testopia-read-only", {'object' => {'type' => 'Testopia administration'}}) unless Bugzilla->user->in_group('admin'); + +local our $plan = Bugzilla::Testopia::TestPlan->new({}); +my $action = $cgi->param('action') || ''; +my $item = $cgi->param('item') || ''; + +if ($item eq 'plan_type'){ + if( $action eq 'edit'){ + my $type_id = $cgi->param('type_id'); + detaint_natural($type_id); + $vars->{'type'} = $plan->plan_type_ref($type_id) + || ThrowUserError("invalid-test-id-non-existent", {'id' => $type_id, 'type' => 'Plan Type'}); + $template->process("testopia/admin/plantypes/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($action eq 'doedit'){ + my $type_id = $cgi->param('type_id'); + my $type_name = $cgi->param('name') || ''; + my $type_desc = $cgi->param('description') || ''; + + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}) if $type_name eq ''; + detaint_natural($type_id); + ThrowUserError("invalid-test-id-non-existent", + {'id' => $type_id, 'type' => 'Plan Type'}) unless $plan->plan_type_ref($type_id); + trick_taint($type_name); + trick_taint($type_desc); + + $plan->update_plan_type($type_id, $type_name, $type_desc); + display(); + } + elsif ($action eq 'add'){ + $template->process("testopia/admin/plantypes/add.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($action eq 'doadd'){ + my $type_name = $cgi->param('name') || ''; + my $type_desc = $cgi->param('description') || ''; + + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}) if $type_name eq ''; + trick_taint($type_name); + trick_taint($type_desc); + ThrowUserError('testopia-name-not-unique', + {'object' => 'Plan Type', 'name' => $type_name}) if $plan->check_plan_type($type_name); + + $plan->add_plan_type($type_name, $type_desc); + display(); + } + else{ + display(); + } + +} + +else { + $template->process("testopia/admin/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + +sub display { + $vars->{'plan'} = $plan; + $template->process("testopia/admin/plantypes/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} diff --git a/tr_attachment.cgi b/tr_attachment.cgi new file mode 100755 index 0000000..e6b58fa --- /dev/null +++ b/tr_attachment.cgi @@ -0,0 +1,214 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Attachment; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; + +Bugzilla->login(LOGIN_REQUIRED); +Bugzilla->error_mode(ERROR_MODE_AJAX) if $cgi->param('ctype') && $cgi->param('ctype') eq 'json'; + +my $action = $cgi->param('action') || ''; +my $attach_id = $cgi->param('attach_id'); + +detaint_natural($attach_id) if $attach_id; +if (!$attach_id and $cgi->param('ctype') ne 'json'){ + print $cgi->header(); + $template->process("testopia/attachment/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +################## +### Edit ### +################## +if ($action eq 'edit') { + print $cgi->header; + validate_test_id($attach_id,'attachment'); + my $attachment = Bugzilla::Testopia::Attachment->new($attach_id); + + ThrowUserError('testopia-permission-denied', {'object' => $attachment}) unless $attachment->canedit; + + $attachment->set_description($cgi->param('description')) if $cgi->param('description'); + $attachment->set_filename($cgi->param('filename')) if $cgi->param('filename'); + $attachment->set_mime_type($cgi->param('mime_type')) if $cgi->param('mime_type'); + $attachment->update(); + + print "{success: true}"; +} + +#################### +### Unlink ### +#################### + +elsif ($action eq 'remove') { + print $cgi->header; + my $item = $cgi->param('object'); + my $item_id = $cgi->param('object_id'); + my $obj; + + if ($item eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($item_id); + } + elsif ($item eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($item_id); + } + elsif ($item eq 'caserun'){ + $obj = Bugzilla::Testopia::TestCaseRun->new($item_id); + $obj = $obj->case; + } + + ThrowUserError('testopia-missing-parameter', {'param' => 'case_id or plan_id'}) unless $obj; + + foreach my $attach_id (split(',', $cgi->param('attach_ids'))){ + validate_test_id($attach_id,'attachment'); + my $attachment = Bugzilla::Testopia::Attachment->new($attach_id); + + ThrowUserError('testopia-no-delete', {'object' => $attachment}) unless $attachment->candelete; + + if ($obj->type eq 'plan'){ + $attachment->unlink_plan($obj->id); + } + elsif ($obj->type eq 'case'){ + $attachment->unlink_case($obj->id); + } + } + + print "{success: true}"; +} + +elsif ($action eq 'add'){ + print $cgi->header; + + my $item = $cgi->param('object'); + my $item_id = $cgi->param('object_id'); + my $obj; + my $att; + if ($item eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($item_id); + $att->{'case_id'} = $obj->id; + } + elsif ($item eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($item_id); + $att->{'plan_id'} = $obj->id; + } + elsif ($item eq 'caserun'){ + $obj = Bugzilla::Testopia::TestCaseRun->new($item_id); + $att->{'caserun_id'} = $obj->id; + $att->{'case_id'} = $obj->case_id; + } + + ThrowUserError("testopia-read-only", {'object' => $obj}) unless $obj->canedit; + + defined $cgi->upload('data') + || ThrowUserError("file_not_specified"); + + my $fh = $cgi->upload('data'); + my $data; + # enable 'slurp' mode + local $/; + + $data = <$fh>; + $data || ThrowUserError("zero_length_file"); + + $att->{'submitter_id'} = Bugzilla->user->id; + $att->{'description'} = $cgi->param("description") || 'Attachment'; + $att->{'filename'} = $cgi->upload("data"); + $att->{'mime_type'} = $cgi->uploadInfo($cgi->param("data"))->{'Content-Type'}; + $att->{'contents'} = $data; + + my $attachment = Bugzilla::Testopia::Attachment->create($att); + + print "{success: true}"; +} + +################ +### List ### +################ +elsif ($action eq 'list') { + my $format = $template->get_format("testopia/attachment/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); + print $cgi->header; + + my $item = $cgi->param('object'); + my $item_id = $cgi->param('object_id'); + my $obj; + + if ($item eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($item_id); + } + elsif ($item eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($item_id); + } + elsif ($item eq 'caserun'){ + $obj = Bugzilla::Testopia::TestCaseRun->new($item_id); + } + + my @attachments = @{$obj->attachments}; + + my $out = ''; + $out .= '{'; + $out .= '"totalResultsAvailable":' . scalar @attachments .','; + $out .= '"attachment":['; + foreach my $i (@attachments){ + $out .= $i->TO_JSON($cgi) . ',' if $i->canview; + } + chop($out) if scalar @attachments; + $out .= ']}'; + + $vars->{'json'} = $out; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); + exit; +} + +################ +### View ### +################ +else { + validate_test_id($attach_id,'attachment'); + my $attachment = Bugzilla::Testopia::Attachment->new($attach_id); + + ThrowUserError("attachment_removed") if $attachment->datasize == 0; + + my $filename = $attachment->filename; + $filename =~ s/\\/\\\\/g; # escape backslashes + $filename =~ s/"/\\"/g; # escape quotes + + print $cgi->header(-type => $attachment->mime_type . "; name=\"$filename\"", + -content_disposition => "inline; filename=\"$filename\"", + -content_length => $attachment->datasize); + disable_utf8(); + print $attachment->contents; +} diff --git a/tr_builds.cgi b/tr_builds.cgi new file mode 100755 index 0000000..0994eed --- /dev/null +++ b/tr_builds.cgi @@ -0,0 +1,165 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Constants; +use Bugzilla::Testopia::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Util; + +use JSON; +#TODO: Add a way to filter name + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $action = $cgi->param('action') || ''; +my $product_id = $cgi->param('product_id'); + +print "Location: tr_show_product.cgi?tab=build\n\n" unless $action; + +print $cgi->header; + +ThrowUserError("testopia-missing-parameter", {param => "product_id"}) unless $product_id; + +my $product = Bugzilla::Testopia::Product->new($product_id); + +###################### +### Create a Build ### +###################### +if ($action eq 'add'){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + my $build = Bugzilla::Testopia::Build->create({ + product_id => $product->id, + name => $cgi->param('name') || '', + description => $cgi->param('desc') || $cgi->param('description') || '', + milestone => $cgi->param('milestone') || '---', + isactive => $cgi->param('isactive') ? 1 : 0, + }); + + print "{success: true, build_id: ". $build->id . "}"; +} + +#################### +### Edit a Build ### +#################### +elsif ($action eq 'edit'){ + + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + my $build = Bugzilla::Testopia::Build->new($cgi->param('build_id')); + + $build->set_name($cgi->param('name')) if $cgi->param('name'); + $build->set_description($cgi->param('description')) if $cgi->param('description'); + $build->set_milestone($cgi->param('milestone')) if $cgi->param('milestone'); + $build->set_isactive($cgi->param('isactive') =~ /(1|true)/ ? 1 : 0) if $cgi->param('isactive'); + + $build->update(); + print "{success: true}"; +} + +elsif ($action eq 'list'){ + ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canview; + my $json = new JSON; + my @builds; + my $activeonly = $cgi->param('activeonly'); + my $current = Bugzilla::Testopia::Build->new($cgi->param('current_build') || {}); + my $out; + + trick_taint($activeonly) if $activeonly; + + foreach my $b (@{$product->builds($activeonly)}){ + push @builds, $b if $b->id != $current->id; + } + unshift @builds, $current if defined $current->id; + + $out .= $_->TO_JSON . ',' foreach (@builds); + chop ($out); # remove the trailing comma for IE + + print "{builds:[$out]}"; + +} + +elsif ($action eq 'report'){ + ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canview; + my $vars = {}; + my $template = Bugzilla->template; + + print $cgi->header; + + my @build_ids = $cgi->param('build_ids'); + my @builds; + my @bug_ids; + + foreach my $g (@build_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::Build->new($id); + push @builds, $obj if $obj->product->canview; + $obj->bugs; + push @bug_ids, $obj->{'bug_list'}; + } + } + + my $total = $builds[0]->case_run_count(undef, \@builds); + my $passed = $builds[0]->case_run_count(PASSED, \@builds); + my $failed = $builds[0]->case_run_count(FAILED, \@builds); + my $blocked = $builds[0]->case_run_count(BLOCKED, \@builds); + my $idle = $builds[0]->case_run_count(IDLE, \@builds); + my $error = $builds[0]->case_run_count(ERROR, \@builds); + + my $completed = $passed + $failed + $blocked; + + my $unfinished = $total - $completed; + my $unpassed = $completed - $passed; + my $unfailed = $completed - $failed; + my $unblocked = $completed - $blocked; + + $vars->{'total'} = $total; + $vars->{'completed'} = $completed; + $vars->{'passed'} = $passed; + $vars->{'failed'} = $failed; + $vars->{'blocked'} = $blocked; + $vars->{'idle'} = $idle; + $vars->{'error'} = $error; + + $vars->{'percent_completed'} = calculate_percent($total, $completed); + $vars->{'percent_passed'} = calculate_percent($completed, $passed); + $vars->{'percent_failed'} = calculate_percent($completed, $failed); + $vars->{'percent_blocked'} = calculate_percent($completed, $blocked); + $vars->{'percent_idle'} = calculate_percent($total, $idle); + $vars->{'percent_error'} = calculate_percent($total, $error); + + $vars->{'builds'} = join(',',@build_ids); + $vars->{'bugs'} = join(',',@bug_ids); + $vars->{'bug_count'} = scalar @bug_ids; + + $template->process("testopia/reports/completion.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + +} \ No newline at end of file diff --git a/tr_case_reports.cgi b/tr_case_reports.cgi new file mode 100755 index 0000000..a84870e --- /dev/null +++ b/tr_case_reports.cgi @@ -0,0 +1,133 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Maciej Maczynski are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +# Portions taken from Bugzilla reports by Gervase Markham + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Report; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); + +my $type = $cgi->param('type') || ''; + +if ($type eq 'status-breakdown'){ + my $case_id = trim(Bugzilla->cgi->param('case_id') || ''); + + unless ($case_id){ + $vars->{'form_action'} = 'tr_case_reports.cgi'; + $template->process("testopia/case/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } + validate_test_id($case_id, 'case'); + + my $case = Bugzilla::Testopia::TestCase->new($case_id); + exit unless $case->canview; + + my @data; + my $caserun = Bugzilla::Testopia::TestCaseRun->new({}); + + my @names; + my @values; + foreach my $status (@{$caserun->get_status_list}){ + push @names, $status->{'name'}; + push @values, $case->get_caserun_count($status->{'id'}); + } + push @data, \@names; + push @data, \@values; + + $vars->{'width'} = 200; + $vars->{'height'} = 150; + $vars->{'data'} = \@data; + $vars->{'chart_title'} = 'Historic Status Breakdown'; + $vars->{'colors'} = (['#858aef', '#56e871', '#ed3f58', '#b8eae1', '#f1d9ab', '#e17a56']); + print $cgi->header; + $template->process("testopia/reports/report-pie.png.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else{ + $cgi->param('current_tab', 'case'); + $cgi->param('viewall', 1); + my $report = Bugzilla::Testopia::Report->new('case', 'tr_list_cases.cgi', $cgi); + $vars->{'report'} = $report; + $vars->{'qname'} = $cgi->param('qname'); + + ### From Bugzilla report.cgi by Gervase Markham + my $formatparam = $cgi->param('format'); + my $report_action = $cgi->param('report_action'); + if ($report_action eq "data") { + # So which template are we using? If action is "wrap", we will be using + # no format (it gets passed through to be the format of the actual data), + # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else). + # report.html.tmpl produces an HTML framework for either tables of HTML + # data, or images generated by calling report.cgi again with action as + # "plot". + $formatparam =~ s/[^a-zA-Z\-]//g; + trick_taint($formatparam); + $vars->{'format'} = $formatparam; + $formatparam = ''; + } + elsif ($report_action eq "plot") { + # If action is "plot", we will be using a format as normal (pie, bar etc.) + # and a ctype as normal (currently only png.) + $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0; + $vars->{'x_labels_vertical'} = $cgi->param('x_labels_vertical') ? 1 : 0; + $vars->{'data'} = $report->{'image_data'}; + } + else { + ThrowCodeError("unknown_action", {action => $cgi->param('report_action')}); + } + + my $format = $template->get_format("testopia/reports/report", $formatparam, + scalar($cgi->param('ctype'))); + + my @time = localtime(time()); + my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; + my $filename = "report-" . $date . ".$format->{extension}"; + + my $disp = "inline"; + # We set CSV files to be downloaded, as they are designed for importing + # into other programs. + if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + } + + print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + + $vars->{'time'} = $date; + $template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); + + exit; +} diff --git a/tr_caserun.cgi b/tr_caserun.cgi new file mode 100755 index 0000000..af2d1e2 --- /dev/null +++ b/tr_caserun.cgi @@ -0,0 +1,202 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Bug; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::Constants; + +use JSON; + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $caserun; +my $action = $cgi->param('action') || ''; + +if ($cgi->param('caserun_id')){ + $caserun = Bugzilla::Testopia::TestCaseRun->new($cgi->param('caserun_id')); +} +elsif ($cgi->param('run_id')){ + $caserun = Bugzilla::Testopia::TestCaseRun->new($cgi->param('run_id'), + $cgi->param('case_id'), + $cgi->param('build_id'), + $cgi->param('env_id')); +} +else{ + print $cgi->header; + ThrowUserError('testopia-missing-parameter', {'param' => 'caserun_id or case_id and run_id'}); +} + +if ($action eq 'update_build'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + my $build_id = $cgi->param('build_id'); + detaint_natural($build_id); + validate_test_id($build_id, 'build'); + + $caserun = $caserun->switch($build_id, $caserun->environment->id); + + print "{'success': true, caserun:" . $caserun->TO_JSON ."}"; +} + +elsif ($action eq 'update_environment'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + my $environment_id = $cgi->param('caserun_env'); + detaint_natural($environment_id); + validate_test_id($environment_id, 'environment'); + + $caserun = $caserun->switch($caserun->build->id, $environment_id); + + print "{'success': true, caserun:" . $caserun->TO_JSON ."}"; +} + +elsif ($action eq 'update_status'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + my $status_id = $cgi->param('status_id'); + + detaint_natural($status_id); + + $caserun->set_status($status_id, $cgi->param('update_bug')); + + print "{'success': true}"; +} + +elsif ($action eq 'update_note'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + my $note = $cgi->param('note'); + + trick_taint($note); + $caserun->append_note($note); + + print "{'success': true}"; +} + +elsif ($action eq 'update_assignee'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + + my $assignee_id = login_to_id(trim($cgi->param('assignee')),'THROW_ERROR'); + + $caserun->set_assignee($assignee_id); + + print "{'success': true}"; +} + +elsif ($action eq 'update_sortkey'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + + my $sortkey = $cgi->param('sortkey'); + ThrowUserError("number_not_numeric", {'num' => $sortkey, field => 'index', field_descs =>{'index' => 'index'}}) unless $caserun->canedit; + + $caserun->set_sortkey($sortkey); + + print "{'success': true}"; +} + +elsif ($action eq 'update_priority'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + + $caserun->case->set_priority($cgi->param('priority')); + $caserun->case->update(); + + print "{'success': true, caserun:" . $caserun->TO_JSON ."}"; + +} + +elsif ($action eq 'update_category'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + + $caserun->case->set_category($cgi->param('category')); + $caserun->case->update(); + + print "{'success': true, caserun:" . $caserun->TO_JSON ."}"; + +} + +elsif ($action eq 'getbugs'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit; + my $bugs; + foreach my $bug (@{$caserun->bugs}){ + $bugs->{'summary'} = $bug->short_desc; + $bugs->{'bug_id'} = $bug->bug_id; + } + print '{"bugs":'; + print to_json($bugs); + print '}'; +} + +elsif ($action eq 'gettext'){ + unless ($caserun->canview){ + print $cgi->header; + ThrowUserError("testopia-permission-denied", {'object' => $caserun}); + } + + + + my $text = $caserun->case->text; + $text->{'notes'} = $caserun->notes; + $text->{'case_id'} = $caserun->case->id; + $text->{'summary'} = $caserun->case->summary; + + $vars->{'text'} = $text; + + print $cgi->header(-type => 'text/xml'); + Bugzilla->template->process("testopia/case/text.xml.tmpl", $vars) || + ThrowTemplateError(Bugzilla->template->error()); +} + +elsif ($action eq 'gethistory'){ + print $cgi->header; + ThrowUserError("testopia-permission-denied", {'object' => $caserun}) unless $caserun->canview; + + print '{"records":'; + print to_json($caserun->get_history); + print '}'; + +} +else { + print "Location: tr_show_run.cgi\n\n"; +} diff --git a/tr_caserun_reports.cgi b/tr_caserun_reports.cgi new file mode 100644 index 0000000..0b0edf0 --- /dev/null +++ b/tr_caserun_reports.cgi @@ -0,0 +1,100 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +# Portions taken from Bugzilla reports by Gervase Markham + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Report; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); + +my $type = $cgi->param('type') || ''; + +if ($type eq 'status-breakdown'){ +} +else{ + $cgi->param('current_tab', 'case_run'); + $cgi->param('viewall', 1); + my $report = Bugzilla::Testopia::Report->new('caserun', 'tr_list_caseruns.cgi', $cgi); + $vars->{'report'} = $report; + $vars->{'qname'} = $cgi->param('qname'); + + ### From Bugzilla report.cgi by Gervase Markham + my $formatparam = $cgi->param('format'); + my $report_action = $cgi->param('report_action'); + if ($report_action eq "data") { + # So which template are we using? If action is "wrap", we will be using + # no format (it gets passed through to be the format of the actual data), + # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else). + # report.html.tmpl produces an HTML framework for either tables of HTML + # data, or images generated by calling report.cgi again with action as + # "plot". + $formatparam =~ s/[^a-zA-Z\-]//g; + trick_taint($formatparam); + $vars->{'format'} = $formatparam; + $formatparam = ''; + } + elsif ($report_action eq "plot") { + # If action is "plot", we will be using a format as normal (pie, bar etc.) + # and a ctype as normal (currently only png.) + $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0; + $vars->{'x_labels_vertical'} = $cgi->param('x_labels_vertical') ? 1 : 0; + $vars->{'data'} = $report->{'image_data'}; + } + else { + ThrowCodeError("unknown_action", {action => $cgi->param('report_action')}); + } + + my $format = $template->get_format("testopia/reports/report", $formatparam, + scalar($cgi->param('ctype'))); + + my @time = localtime(time()); + my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; + my $filename = "report-" . $date . ".$format->{extension}"; + + my $disp = "inline"; + # We set CSV files to be downloaded, as they are designed for importing + # into other programs. + if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + } + + print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + + $vars->{'time'} = $date; + $template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); + + exit; +} diff --git a/tr_categories.cgi b/tr_categories.cgi new file mode 100755 index 0000000..d068d4d --- /dev/null +++ b/tr_categories.cgi @@ -0,0 +1,103 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use JSON; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $action = $cgi->param('action') || ''; +my $product_id = $cgi->param('product_id'); + +print "Location: tr_show_product.cgi?tab=category\n\n" unless $action; + +print $cgi->header; + +ThrowUserError("testopia-missing-parameter", {param => "product_id"}) unless $product_id; + +my $product = Bugzilla::Testopia::Product->new($product_id); + +######################### +### Create a Category ### +######################### + +if ($action eq 'add'){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + my $category = Bugzilla::Testopia::Category->create({ + product_id => $product->id, + name => $cgi->param('name'), + description => $cgi->param('desc'), + }); + + print "{success: true, category_id: ". $category->id . "}"; +} + +####################### +### Edit a Category ### +####################### +elsif ($action eq 'edit'){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + my $category = Bugzilla::Testopia::Category->new($cgi->param('category_id')); + + $category->set_name($cgi->param('name')) if $cgi->param('name'); + $category->set_description($cgi->param('description')) if $cgi->param('description'); + + $category->update; + print "{success: true}"; +} + +######################### +### Delete a Category ### +######################### +elsif ($action eq 'delete'){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + my $category = Bugzilla::Testopia::Category->new($cgi->param('category_id')); + ThrowUserError("testopia-non-zero-case-count") unless $category->candelete; + + $category->remove; + + print "{success: true}"; + +} + +elsif ($action eq 'list'){ + ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canview; + my $json = new JSON; + + my $out; + $out .= $_->TO_JSON . ',' foreach (@{$product->categories()}); + chop ($out); # remove the trailing comma for IE + + print "{categories:[$out]}"; + +} diff --git a/tr_csv2xml.pl b/tr_csv2xml.pl new file mode 100644 index 0000000..282cbf3 --- /dev/null +++ b/tr_csv2xml.pl @@ -0,0 +1,701 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Dawn Endico +# David Koenig + +use strict; + +use Class::CSV; +use Getopt::Long; +use File::Temp "tempfile"; +use Pod::Usage; +use lib qw(. lib); + +=head1 NAME + +tr_csv2xml.pl - Convert CSV file to Testopia XML format. + +=head1 SYNOPSIS + + tr_csv2xml.pl [ Options ] csvfilename xmlfilename + +=head1 OPTIONS + +=over 8 + +=item B<-h --help> + +This usage statement. + +=item B<-t --tcdb> + +Preprocess CSV file to correct problems with TCDB CSV format. + +=item B<-u --usage> + +This usage statement. + +=head1 DESCRIPTION + +This script converts the file csvfilename in CSV format to the file +xmlfilename in Testopia XML format. + +=cut + +my $TEST_PLAN_AUTHOR = "Change TEST_PLAN_AUTHOR in import.pl user\@novell.com"; +use constant TEST_PLAN_DOCUMENT => "Change TEST_PLAN_DOCUMENT in import.pl"; +use constant TEST_PLAN_NAME => "Change TEST_PLAN_NAME"; +use constant TEST_PLAN_PRODUCT => "TestProduct"; +use constant TEST_PLAN_PRODUCT_VERSION => "other"; +# Test Plan types are: Database_id, Database_description or Xml_description. +use constant TEST_PLAN_NAME_TYPE => "Xml_description"; + +my $debug = 0; +my $tcdb = 0; +my $usage = 0; + +# +#Display error message to stderr and exit. +# +sub error { + my ($reason,$errtype) = @_; + print STDERR $reason . ".\n"; + exit(1); +} + +# +# Creat mapping from TCDB user ids to email addresses. +# +sub map_TCDB_users +{ + my ($tcdb_user) = @_; + if ( -r "tcdbUsers" ) + { + open(TCDBUSERS, "tcdbUsers") || error("Cannot open tcdbUsers"); + while () + { + chop; + my ($email_addr,$user_id) = split(/ /); + $tcdb_user->{$user_id} = $email_addr; + } + close(TCDBUSERS); + } +} + +# +# Peek ahead one character in the file. +# +sub peek(*) +{ + my $fh = shift; + + # Get current position in file. + my $position = tell $fh; + # Read next character. + read(CSVINPUT, my $next_char, 1); + # Restore position in file. + seek(CSVINPUT,$position,0); + + return $next_char; +} + +# +# Print the fields. +# +# In each field need to change '\"' to '"' and change all '"' to '""'. The last field +# will also have a '"' at the end of the line that needs to be removed. +# +# If TCDB format the Test Case Name may contain a series of 4-6 digits after two underscores +# which need to be removed. The TCDB appended the Test Case number to any Test Case created +# from a existing Test Case. In Testopia the Test Case numbers have no meaning. +# +sub print_fields +{ + my ($file_descriptor,$fields_ref,$testcasenamefield,$tcdb_format) = @_; + + my $index = 0; + while ( $index < @$fields_ref ) + { + $fields_ref->[$index] =~ s/"$//g if ( $index == ( @$fields_ref -1 ) ); + $fields_ref->[$index] =~ s/\\"/"/g; + $fields_ref->[$index] =~ s/"/""/g; + if ( $tcdb_format && ( $index == $testcasenamefield ) ) + { + $fields_ref->[$index] =~ s/__\d\d\d\d\d\d\d//g; + $fields_ref->[$index] =~ s/__\d\d\d\d\d\d//g; + $fields_ref->[$index] =~ s/__\d\d\d\d\d//g; + } + print $file_descriptor "\"$fields_ref->[$index]\""; + print $file_descriptor "," if ( $index != ( @$fields_ref -1 ) ); + $index += 1; + } + print $file_descriptor "\n"; +} + +# +# +# Create the work file from the input file that will be passed to Class::CSV. The input file's +# first line is field descriptions which are not passed into Class::CSV->parse. +# +# The Test Case Data Base (TCDB) CSV files also need to be processed to clean up format errors. +# +# The TCDB CSV errors are: +# 1) Does not escape " used in a field with "". May use \" instead of "" but not always. +# 2) Runs the CSV across multiple lines. +# 3) In some cases a line may be missing the last field. +# +sub remove_field_list +{ + my ($input_filename,$work_filename,$tcdb_format) = @_; + my $field_list = ""; + my @field_buffer; + my @fields; + my $fields_index = 0; + my $in_quote_field = 0; + my $line = ""; + my $line_count = 0; + my $number_of_fields = 0; + my $parse_line = ""; + my $testcasenamefield = ""; + + open(CSVINPUT, $input_filename) or error("Cannot open file $input_filename"); + open(CSVWORK, ">", $work_filename) or error("Cannot open file $work_filename"); + + while () + { + chop; + + s/\r//g; + # + # Map extended characters into HTML entities. + # + s/\342\200\223/-/g; + s/\342\200\224/—/g; + s/\342\200\230/‘/g; + s/\342\200\231/’/g; + s/\342\200\234/“/g; + s/\342\200\235/”/g; + s/\342\200\246/…/g; + s/\302\240/ /g; + s/\302\251/©/g; + s/\031/'/g; + s/\221/&8216;/g; # left single quotation mark + s/\222/&8217;/g; # right single quotation mark + s/\223/&8220;/g; # left double quotation mark + s/\224/&8221;/g; # right double quotation mark + s/\226/-/g; + s/\337/ß/g; # beta + s/\341/à/g; # small letter a with acute accent + s/\342/á/g; # small letter a with grave accent + s/\344/ä/g; # small letter a with tilde + s/\346/å/g; # small letter a with umlaut + s/\347/æ/g; # small ae + s/\350/ç/g; # small letter c cedilla + s/\351/è/g; # small letter e with acute accent + s/\364/ô/g; # small letter o with circumflex + + $line_count += 1; + if ( $line_count == 1 ) + { + $field_list = $_; + $number_of_fields = $field_list; + $number_of_fields =~ s/[^,]//g; + # Counting the number of commas so number of fields is one more. + $number_of_fields = (length $number_of_fields) + 1; + # Returned $field_list needs to be lower case, all spaces, " and \ removed. + $field_list = lc $field_list; + $field_list =~ s/[\s"\/]//g; + my $index = 0; + # Find the field that contains the Test Case name. + foreach my $field ( split(/,/,$field_list) ) + { + if ( $field eq "testcasename" ) + { + $testcasenamefield = $index ; + last; + } + $index++; + } + next; + } + + if ( ! $tcdb_format ) + { + print CSVWORK $_ . "\n"; + next; + } + + # Missing or empty environment in TCDB has this value in the environment field. Set it to + # null and hope it was not in the middle of a field. + s/"\$EMPTYENV"/""/; + + # TCDB CSV options that are not handled correctly: + # If a field contains some thing like: + # '2. Click "Roles and Tasks " , "Storage" , click "Volumes" and'' + # the "," will be seen as field seperator and not as part of the field. + + # Add the current line onto the line to parse. + $parse_line .= $_; + + # The end of the TCDB CSV line will be a double quote at the end of the line. Keep combining + # lines until we have a double quote at the end of the line and try to parse the line. + if ( ! ($parse_line =~ /.+\n*"$/) ) + { + $parse_line .= "\\n"; + next; + } + + # At this point $parse_line will hopefully contain the full CSV line. It may not though since + # we could have found a double quote at the end of the line used in field that spans multiple + # lines. Parse it below and if we have not found all the fields we will loop back and read + # more lines until the next double quote at the end of the line is found. + + $in_quote_field = 0; + my $index = 0; + @fields = (); + @field_buffer = (); + my @chars = split(//,$parse_line); + while ( $index <= $#chars ) + { + my $char = $chars[$index]; + if ( $char eq "\"" ) + { + # Following check is for character sequence \". Look at last character in the current + # field_buffer and if it is a \ this " is not the end of the field. + if ( $#field_buffer>=0 && $field_buffer[$#field_buffer] eq "\\" ) + { + push (@field_buffer,$char); + } + elsif ( ! $in_quote_field ) + { + $in_quote_field = 1; + } + else + { + # If this double quote is followed by a comma double quote ',"' it would be the end of the field + # otherwise it should included in the field. Need to ignore white space when searching for the + # next two characters. + # The TCDB never breaks a line at field separators (have not seen it yet anyway) so the code does + # not need to worry about finding a comma at the end of the line and checking for a double quote at + # the beginning of the next line. + my $comma_index = $index+1; + while ( $comma_index<=$#chars ) + { + last if ( $chars[$comma_index] =~ m/\S/ ); + $comma_index++; + } + my $double_quote_index = $comma_index+1; + while ( $double_quote_index<=$#chars ) + { + last if ( $chars[$double_quote_index] =~ m/\S/ && $chars[$double_quote_index] ne ',' ); + $double_quote_index++; + } + # Is the next non-white space character a comma followed by a double quote? If yes then we + # have reached the end of the field. + if ( ( $comma_index <= $#chars && $chars[$comma_index] eq "," ) && + ( $double_quote_index <= $#chars && $chars[$double_quote_index] eq "\"" ) ) + { + push (@fields,join("",@field_buffer)); + @field_buffer = (); + # Skip past the comma. + $index++; + $in_quote_field = 0; + } + # This quote is at end of the line. Assume it's the last double quote on the CSV line. + elsif ( $index == $#chars ) + { + push (@fields,join("",@field_buffer)); + @field_buffer = (); + $in_quote_field = 0; + } + else + { + push (@field_buffer,$char); + } + + } + } + # + # This check is for empty fields, i.e. "field1",,"field2". + # + elsif ( $char eq "," && ! $in_quote_field ) + { + push (@fields,""); + } + else + { + if ( $in_quote_field ) + { + push (@field_buffer,$char); + } + else + { + # Only allow white space between fields. + error("Found unexpected character $char after phrase '" . + join("",@field_buffer) . + "' on line $line_count in file $input_filename") if ( $char =~ '\S'); + } + } + $index++; + } + + my $next_char = peek(*CSVINPUT); + my $looks_like_end_of_csv_line = $next_char eq "\"" || $next_char eq ""; + + # Do we have all the fields we need? + if ( ($#fields == ($number_of_fields-1)) && (! $in_quote_field) && $looks_like_end_of_csv_line ) + { + print_fields(\*CSVWORK,\@fields,$testcasenamefield,$tcdb_format); + $parse_line = ""; + @fields = (); + } + # Is this the TCDB export error? We have one less field than needed and the next line begins with a + # double quote. + elsif ( ($#fields == ($number_of_fields-2)) && (! $in_quote_field ) && $looks_like_end_of_csv_line ) + { + if ( $_ =~ m/^"/ ) + { + # Pull double quote of the end of the last field. + $fields[$#fields] =~ s/"$//; + # Create the missing field. Need to insert a double quote since print_fields expects a double + # quote at end of last field. + push (@fields,"\""); + print_fields(\*CSVWORK,\@fields,$testcasenamefield,$tcdb_format); + $parse_line = ""; + @fields = (); + } + } + elsif ( $#fields >= $number_of_fields ) + { + error("Read too many lines. Parse line '$parse_line' at line $line_count in file $input_filename"); + } + else + # Not enough fields yet. Need to append a \n to the $parse_line buffer and starting reading + # until we find another double quote at the end of the line. + { + $parse_line .= "\\n"; + } + } + # When End of File is read the parse_line is suppose to be empty. + error("Reached end of file while parsing '$parse_line'") if ( $parse_line ne "" ); + close(CSVINPUT); + close(CSVWORK); + error("Did not find the last double quote in file") if ( $in_quote_field ); + + # + # Sort the corrected CSV to remove duplicate records. + # + if ( $tcdb ) + { + system("sort -u -o " . $work_filename . " " . $work_filename) == 0 or error("Could not sort $work_filename"); + } + + return $field_list; +} + +# +# Remove the leading and trailing white space characters. Remove commas at end of line. +# +sub remove_white_space { + my ($line) = @_; + $line =~ s/^\s+//g; + $line =~ s/\s+$//g; + $line =~ s/,$//g; + + return $line; +} + +# +# Characters that are entities in XML and HTML need to be +# converted to their entity representation. +# +# Some new lines have been showing up as \\n in exports. +# +sub fix_entities { + my ($line) = @_; + $line =~ s/\\n/\n/g; + $line =~ s/\&/&/g; + $line =~ s/\/>/g; + $line =~ s/\'/'/g; + $line =~ s/\"/"/g; + + return $line; +} + +GetOptions("debug" => \$debug, "tcdb" => \$tcdb, "help|usage|?" => \$usage); + +pod2usage(0) if $usage; + +error("Must supply a CSV file to convert") if ( $#ARGV == -1 ); +error("Need to supply XML output file") if ( $#ARGV == 0 ); +error("Too many arguments") if ( $#ARGV >= 2 ); + +my $csv_input_filename = $ARGV[0]; +my $xml_output_filename = $ARGV[1]; +my $csv_work_filename = $csv_input_filename . ".work"; +open(XMLOUTPUT, ">", $xml_output_filename) or error("Cannot open file $xml_output_filename"); +my %tcdb_user; +my $field_list = remove_field_list($csv_input_filename,$csv_work_filename,$tcdb); +map_TCDB_users(\%tcdb_user) if ( $tcdb ); + +# +# Process the $field_list variable which comes from the first line of the CSV file. This line +# defines the columns and column order of the CSV file. +# +# Format of the first line should be in the form: +# "Testcase Name","Attributes","Priority","Description","Folder","Creator","Owner", +# "Pass/Fail Definition","Setup Steps","Cleanup Steps","Steps" +# +# Columns currently used if they exist are: +# attributes - split apart at each comma to become a tag. +# category - category for test case. +# cleanupsteps - added to Break Down section. +# component - component for test case. +# description - summary unless testcasename is defined. added to Action section if -tcdb flag +# used. +# environment - split apart at each comma to become a tag. +# folder - only processed if -tcdb option is supplied. split apart at each '/'. based on each +# teams input one field becomes the category and others tags. each team defines which +# sub folders they want to use. +# longdescription - added to Action section if -tcdb flag used. +# owner (required) - in TCDB this is a ID that is mapped to a email address from the file +# tcdbUsers. +# passfaildefinition - added to Expected Results section. +# priority - becomes the priority. I just a number P is prepended. +# resdetails - added to Action section if -tcdb flag used. +# setupsteps - added to Set Up section. +# steps - added to Action section. +# testcasename - becomes the summary. if testcasename is not supplied the description +# is the summary. if testcasename and description are both null a error is +# generated. added to Action section if -tcdb flag used. +# +# The order of the columns is not important. The columns supplied to Class::CSV will be in +# order found on the first line of the CSV file. +# +# The field_list returned from remove_field_list() will have been: +# Transformed to lower case. +# All white space characters removed. +# All double quotes (") removed. +# All forward slashes (/) removed. +# + +# Column name mapping. $field_list contains the name of each column in the CSV file. If your +# column name is that same as a default column name you can covert the column name in $field_list +# and no additional code is needed for the field. +# +# For example if you have a column named 'author' that is really the 'owner' of the Test Case you +# just change 'author' to 'owner' in $field_list. +# +# Add , to front and end of $field_list to make substitution logic easier. They are remove when +# substitutions are finished. +$field_list = ",$field_list,"; +# author is mapped to owner +$field_list =~ s/,author,/,owner,/g; +# result is mapped to passfaildefinition +$field_list =~ s/,result,/,passfaildefinition,/g; +# summary is mapped to testcasename +$field_list =~ s/,summary,/,testcasename,/g; +# tags is mapped to attributes +$field_list =~ s/,tags,/,attributes,/g; +# remove , from beginning of $field_list +$field_list =~ s/^,//; +# remove , from end of $field_list +$field_list =~ s/,$//; + +my %fields; +foreach my $field ( split(/,/,$field_list) ) +{ + $fields{$field} = ""; +} + +my $csv = Class::CSV->parse( + filename => $csv_work_filename, + fields => [ split(/,/,$field_list) ] +); + +print XMLOUTPUT "\n"; +print XMLOUTPUT ") +{ + print XMLOUTPUT $_; +} +close(DTDINPUT); +print XMLOUTPUT "]>\n"; +print XMLOUTPUT "\n"; + +if ( $TEST_PLAN_AUTHOR ne "Change TEST_PLAN_AUTHOR in import.pl user\@novell.com" ) +{ + print XMLOUTPUT " \n"; + print XMLOUTPUT " " . TEST_PLAN_NAME . "\n"; + print XMLOUTPUT " " . TEST_PLAN_PRODUCT . "\n"; + print XMLOUTPUT " " . TEST_PLAN_PRODUCT_VERSION . "\n"; + print XMLOUTPUT " " . TEST_PLAN_DOCUMENT . "\n"; + print XMLOUTPUT " \n"; +} + +my $line_count = 0; +foreach my $line (@{$csv->lines()}) { + $line_count += 1; + print XMLOUTPUT " owner(); + $owner = $tcdb_user{$line->owner()} if ( $tcdb ); + error("Could not find owner for Test Case at line $line_count in $csv_work_filename") if ( $owner eq "" ); + + print XMLOUTPUT "author=\"" . fix_entities($owner) . "\" "; + if ( defined($fields{'priority'}) ) + { + my $priority = fix_entities($line->priority()); + $priority =~ s/ .*//g; + $priority = uc $priority; + $priority = "P5" if ( $priority eq "" ); + $priority = "P" . $priority if ( ! ( $priority =~ m/^P.*/ ) ); + print XMLOUTPUT "priority=\"" . fix_entities($priority) . "\" "; + } + print XMLOUTPUT "automated=\"Manual\" "; + print XMLOUTPUT "status=\"CONFIRMED\">\n"; + print XMLOUTPUT " " . TEST_PLAN_NAME . "\n"; + my $summary; + if ( defined($fields{'testcasename'}) ) + { + $summary = fix_entities($line->testcasename()); + } + elsif ( defined($fields{'description'}) ) + { + $summary = fix_entities($line->description()); + } + if ( defined($fields{'environment'}) ) + { + my $environment = $line->environment(); + $summary .= " - " . fix_entities($environment) if ( $environment ne "" ); + } + error("No summary for Test Case at line $line_count in $csv_work_filename") if ( $summary eq "" ); + print XMLOUTPUT " " . $summary . "\n"; + print XMLOUTPUT " " . fix_entities($owner) . "\n"; + if ( $tcdb && defined($fields{'folder'}) ) + { + my @folder = split(/\\/,$line->folder()); + print XMLOUTPUT " " . fix_entities($folder[4]) . "\n" if ( defined( $folder[4] ) ); + if ( defined($fields{'category'}) ) + { + print XMLOUTPUT " " . fix_entities($folder[5]) . "\n" if ( defined( $folder[5] ) ); + } + else + { + print XMLOUTPUT " " . fix_entities($folder[5]) . "\n" if ( defined( $folder[5] ) ); + } + my $fieldstart = 6; + while ( defined $folder[$fieldstart] ) + { + print XMLOUTPUT " " . fix_entities($folder[$fieldstart]) . "\n"; + $fieldstart += 1; + } + } + if ( defined($fields{'attributes'}) ) + { + my @attributes = split(/,/,$line->attributes()); + foreach my $attribute (@attributes) + { + print XMLOUTPUT " " . fix_entities(remove_white_space($attribute)) . "\n"; + } + } + if ( defined($fields{'environment'}) ) + { + my @environments = split(/,/,$line->environment()); + + foreach my $environment (@environments) + { + print XMLOUTPUT " " . fix_entities(remove_white_space($environment)) . "\n"; + } + } + if ( defined($fields{'component'}) && ( $line->component() ne "") ) + { + print XMLOUTPUT " " . fix_entities(remove_white_space($line->component())) . "\n"; + } + if ( defined($fields{'category'}) && ( $line->category() ne "") ) + { + print XMLOUTPUT " " . fix_entities(remove_white_space($line->category())) . "\n"; + } + if ( defined($fields{'setupsteps'}) && ( $line->setupsteps() ne "") ) + { + print XMLOUTPUT " "; + print XMLOUTPUT "[TCDB Setup Steps]\n" if ( $tcdb ); + print XMLOUTPUT fix_entities($line->setupsteps()); + print XMLOUTPUT "\n"; + } + if ( defined($fields{'cleanupsteps'}) && ( $line->cleanupsteps() ne "") ) + { + print XMLOUTPUT " "; + print XMLOUTPUT "[TCDB Cleanup Steps]\n" if ( $tcdb ); + print XMLOUTPUT fix_entities($line->cleanupsteps()); + print XMLOUTPUT "\n"; + } + print XMLOUTPUT " "; + if ( $tcdb ) + { + if ( defined($fields{'testcasename'}) && ( $line->testcasename() ne "") ) + { + print XMLOUTPUT "[TCDB Test Case Name]\n"; + print XMLOUTPUT fix_entities($line->testcasename()); + } + if ( defined($fields{'description'}) && ( $line->description() ne "") ) + { + print XMLOUTPUT "\n\n[TCDB Description]\n"; + print XMLOUTPUT fix_entities($line->description()); + } + if ( defined($fields{'longdescription'}) && ( $line->longdescription() ne "") ) + { + print XMLOUTPUT "\n\n[TCDB Long Description]\n"; + print XMLOUTPUT fix_entities($line->longdescription()); + } + if ( defined($fields{'resdetails'}) && ( $line->resdetails() ne "") ) + { + print XMLOUTPUT "\n\n[TCDB Resolution Details]\n"; + print XMLOUTPUT fix_entities($line->resdetails()); + } + } + if ( defined($fields{'steps'}) && ( $line->steps() ne "") ) + { + print XMLOUTPUT "\n\n[TCDB Steps]\n" if ( $tcdb ); + print XMLOUTPUT fix_entities($line->steps()); + } + print XMLOUTPUT "\n"; + if ( defined($fields{'passfaildefinition'}) && ( $line->passfaildefinition() ne "") ) + { + print XMLOUTPUT " "; + print XMLOUTPUT "[TCDB Pass Fail Definition]\n" if ( $tcdb ); + print XMLOUTPUT fix_entities($line->passfaildefinition()); + print XMLOUTPUT "\n"; + } + print XMLOUTPUT " \n"; + } +print XMLOUTPUT "\n"; + +unlink $csv_work_filename; + +exit 0; + +__END__ + diff --git a/tr_draw.cgi b/tr_draw.cgi new file mode 100644 index 0000000..3d6b762 --- /dev/null +++ b/tr_draw.cgi @@ -0,0 +1,38 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; + +Bugzilla->login(LOGIN_REQUIRED); +my $vars = {}; + +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; +print $cgi->header; + +$vars->{'txt'} = $cgi->param('text'); +$template->process("testopia/text.png.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/tr_environments.cgi b/tr_environments.cgi new file mode 100755 index 0000000..bf317eb --- /dev/null +++ b/tr_environments.cgi @@ -0,0 +1,671 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Brian Kramer +# Michael Hight +# Garrett Braden +# Andrew Nelson + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Classification; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; +use Bugzilla::Testopia::Constants; +use Data::Dumper; +use JSON; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +our $cgi = Bugzilla->cgi; + +local our $vars = {}; +local our $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +local our $env_id = trim( Bugzilla->cgi->param('env_id') ) || ''; + +unless ( $env_id || $action ) { + $template->process( "testopia/environment/choose.html.tmpl", $vars ) + || ThrowTemplateError( $template->error() ); + exit; +} + +########################### +### Environment Actions ### +########################### +if ( $action eq 'add' ) { + ThrowUserError( "testopia-create-denied", + { 'object' => 'Test Environment' } ) + unless Bugzilla->user->in_group('Testers'); + my $name = $cgi->param('name'); + my $product = $cgi->param('product_id'); + + my $env = Bugzilla::Testopia::Environment->create( + { + name => $name, + product_id => $product, + } + ); + + print "{'success': true, 'id': " . $env->id . "}"; +} + +elsif ( $action eq 'delete' ) { + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( 'testopia-no-delete', { 'object' => $env } ) + unless $env->candelete; + + $env->obliterate; + + print "{'success' :true}"; +} + +elsif ( $action eq 'toggle' ) { + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( 'testopia-read-only', { 'object' => $env } ) + unless $env->canedit; + + $env->toggle_archive; + + print "{'success': true}"; +} + +elsif ( $action eq 'rename' ) { + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( "testopia-read-only", { 'object' => $env } ) + unless $env->canedit; + + $env->set_name( $cgi->param('name') ); + $env->update(); + + print "{'success': true}"; +} + +elsif ( $action eq 'clone' ) { + ThrowUserError( "testopia-create-denied", + { 'object' => 'Test Environment' } ) + unless Bugzilla->user->in_group('Testers'); + + my $env = Bugzilla::Testopia::Environment->new($env_id); + my $id = $env->clone( $cgi->param('name'), $cgi->param('product') ); + + print "{'success': true, 'id': " . $id . "}"; +} + +################################## +### Environment Editor Actions ### +################################## + +elsif ( $action eq 'edit' ) { + my $type = $cgi->param('type'); + my $id = $cgi->param('id'); + + for ($type) { + /category/ && do { edit_category($id); }; + /element/ && do { edit_element($id); }; + /property/ && do { edit_property($id); }; + /validexp/ && do { edit_validexp($id); }; + } +} + +elsif ( $action eq 'getChildren' ) { + + my $json = new JSON; + + my $id = $cgi->param('node'); + my $type = $cgi->param('type'); + my $tree_type = $cgi->param('tree_type'); + + trick_taint($id); + trick_taint($type); + + if ( $tree_type =~ /env/ ) { + for ($type) { + /environment/ && do { get_env_categories($id) }; + /category/ && do { get_mapped_category_elements( $env_id, $id ) }; + /element/ && do { get_element_children( $id, 'NODRAG' ) }; + /property/ && do { get_validexp_json( $id, $env_id ) }; + } + } + else { + for ($type) { + /root/ && do { get_root($id) }; + /product/ && do { get_categories($id); }; + /category/ && do { get_category_element_json($id) }; + /element/ && do { get_element_children($id) }; + /property/ && do { get_validexp_json($id) }; + } + } +} + +elsif ( $action eq 'remove_env_node' ) { + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( "testopia-read-only", { 'object' => $env } ) unless $env->canedit; + + foreach my $element_id (split(',', $cgi->param('element_ids'))){ + detaint_natural($element_id); + $env->delete_element($element_id); + } + + print "{'success': true}"; +} + +elsif ( $action eq 'create_child' ) { + my $parent_id = $cgi->param('id'); + my $type = $cgi->param('type'); + + detaint_natural($parent_id); + $parent_id ||= 0; + + for ($type) { + /category/ && do { add_category($parent_id); }; + /element/ && do { add_element($parent_id); }; + /child/ && do { add_element($parent_id, 'CHILD'); }; + /property/ && do { add_property($parent_id); }; + /value/ && do { add_validexp($parent_id); }; + } +} + +elsif ( $action eq 'delete_element' ) { + my $id = $cgi->param('id'); + my $type = $cgi->param('type'); + + trick_taint($id); + + for ($type) { + /category/ && do { delete_category($id); }; + /element/ && do { delete_element($id); }; + /property/ && do { delete_property($id); }; + /validexp/ && do { delete_validexp($id); }; + } + +} + +elsif ( $action eq 'set_selected' ) { + my $property_id = $cgi->param('id'); + my $type = $cgi->param('type'); + + if ( $type =~ /exp/ ) { + my $value = $cgi->param('value'); + + detaint_natural($property_id); + trick_taint($value); + + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( "testopia-read-only", { 'object' => $env } ) unless $env->canedit; + + my $property = Bugzilla::Testopia::Environment::Property->new($property_id); + my $elmnt_id = $property->element_id(); + my $old = $env->get_value_selected( $env->id, $elmnt_id, $property->id ); + $old = undef if $old eq $value; + if ( $env->store_property_value( $property_id, $elmnt_id, $value ) == 0 ) { + $env->update_property_value( $property_id, $elmnt_id, $value ); + } + + print "{'success': true}"; + } +} + +elsif ( $action eq 'apply_element' ) { + my $id = $cgi->param('id'); + my $type = $cgi->param('type'); + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError( "testopia-read-only", { 'object' => $env } ) unless $env->canedit; + + detaint_natural($id); + if ( $type eq "element" ) { + my $element = Bugzilla::Testopia::Environment::Element->new($id); + + my $properties = $element->get_properties; + if ( scalar @$properties == 0 ) { + my $success = $env->store_property_value( 0, $id, "" ); + } + foreach my $property (@$properties) { + my $success = $env->store_property_value( $property->id || 0, $id, "" ); + } + } + #incoming type is a category + else { + my $category = Bugzilla::Testopia::Environment::Category->new($id); + + foreach my $element (@{$category->get_parent_elements}) { + $env->store_property_value( 0, $element->id, "" ); + } + } + print "{'success': true}"; +} + +else { + display(); +} + +sub display { + detaint_natural($env_id); + validate_test_id( $env_id, 'environment' ); + my $env = Bugzilla::Testopia::Environment->new($env_id); + + if ( !defined($env) ) { + my $env = + Bugzilla::Testopia::Environment->new( { 'environment_id' => 0 } ); + $vars->{'environment'} = $env; + $vars->{'action'} = 'do_add'; + $template->process( "testopia/environment/add.html.tmpl", $vars ) + || print $template->error(); + exit; + } + ThrowUserError( "testopia-read-only", { 'object' => $env } ) + unless $env->canview; + my $category = + Bugzilla::Testopia::Environment::Category->new( { 'id' => 0 } ); + if ( Bugzilla->params->{'useclassification'} ) { + $vars->{'allhaschild'} = $category->get_all_child_count; + $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; + $vars->{'type'} = 'classification'; + } + else { + $vars->{'toplevel'} = $category->get_env_product_list; + $vars->{'type'} = 'product'; + } + $vars->{'user'} = Bugzilla->user; + $vars->{'action'} = 'do_edit'; + $vars->{'environment'} = $env; + $template->process( "testopia/environment/show.html.tmpl", $vars ) + || print $template->error(); + +} + +########################### +### Tree Helper Methods ### +########################### + +sub get_root { + my ($product_id) = (@_); + my @products; + + detaint_natural($product_id); + if ($product_id){ + my $product = Bugzilla::Testopia::Product->new($product_id); + return unless $product->canedit; + push @products, + { + id => $product->id, + text => $product->name, + type => 'product', + cls => 'classification', + leaf => scalar @{$product->environment_categories} > 0 ? JSON::false : JSON::true, + draggable => JSON::false, + }; + } + else { + push @products, + { + id => 'GLOBAL', + text => 'GLOBAL ATTRIBUTES', + type => 'product', + cls => 'classification', + draggable => JSON::false, + }; + } + my $json = new JSON; + print $json->encode( \@products ); + +} + +sub get_categories { + my ($product_id) = (@_); + # Handle the special GLOBAL ATTRIBUTES product + $product_id = $product_id eq 'GLOBAL' ? 0 : $product_id; + if ($product_id) { + my $product = Bugzilla::Testopia::Product->new($product_id); + return unless Bugzilla->user->can_see_product( $product->name ); + } + my $category = Bugzilla::Testopia::Environment::Category->new( {} ); + print $category->product_categories_to_json( $product_id ); +} + +sub get_category_element_json { + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + return unless $category->canview; + my $fish = $category->elements_to_json("TRUE"); + print $fish; +} + +sub get_element_children { + my ( $id, $draggable ) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + return unless $element->canview; + print $element->children_to_json($draggable); +} + +sub get_env_categories { + my ($id) = (@_); + my $env = Bugzilla::Testopia::Environment->new($id); + return unless $env->canview; + print $env->categories_to_json(); +} + +sub get_mapped_category_elements { + my ( $id, $cat_id ) = (@_); + my $env = Bugzilla::Testopia::Environment->new($id); + return unless $env->canview; + print $env->mapped_category_elements_to_json($cat_id); +} + +sub get_validexp_json { + my ( $id, $env_id ) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + return unless $property->canview; + print $property->value_to_json($env_id); +} + +sub return_numeric_value { + my ($value) = (@_); + $value =~ s/\D+//; + return $value; +} + +##################### +#Edit Helper Methods# +##################### + +sub edit_category { + my ($id) = (@_); + my $name = $cgi->param('text'); + trick_taint($name); + + my $category = Bugzilla::Testopia::Environment::Category->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $category } ) unless $category->canedit; + + unless ( $category->set_name($name) ) { + ThrowUserError('testopia-name-not-unique', {object => 'category'}); + } + + print "{'success':true}"; + +} + +sub edit_element { + my ($id) = (@_); + my $name = $cgi->param('text'); + trick_taint($name); + + my $element = Bugzilla::Testopia::Environment::Element->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $element } ) unless $element->canedit; + + ThrowUserError('testopia-element-in-use') if ( $element->is_mapped() ); + + unless ( $element->set_name($name) ) { + ThrowUserError('testopia-name-not-unique', {object => 'element'}); + } + + print "{'success':true}"; +} + +sub edit_property { + my ($id) = (@_); + my $name = $cgi->param('text'); + trick_taint($name); + + my $property = Bugzilla::Testopia::Environment::Property->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $property } ) unless $property->canedit; + + ThrowUserError('testopia-element-in-use') if ( $property->is_mapped() ); + + unless ( $property->set_name($name) ) { + ThrowUserError('testopia-name-not-unique', {object => 'property'}); + } + + print "{'success':true}"; +} + +sub edit_validexp { + my ($id) = (@_); + + my $property = Bugzilla::Testopia::Environment::Property->new($id); + + ThrowUserError( "testopia-read-only", { 'object' => $property } ) unless $property->canedit; + + my $name = $cgi->param('text'); + ThrowUserError('testopia-invalid-char') if $name =~ /\|/; + trick_taint($name); + + my $oldname = $cgi->param('oldtext'); + ThrowUserError('testopia-invalid-char') if $oldname =~ /\|/; + trick_taint($oldname); + + my $expressions = $property->validexp(); + + if ($expressions =~ qr($name)){ + ThrowUserError('testopia-name-not-unique', {object => 'property value'}); + } + + # Gather existing values: + my %values; + foreach my $v ( split /\|/, $property->validexp ) { + $values{$v} = 1; + } + + # Delete old value: + delete $values{$oldname}; + my $exp = join( "|", keys %values ); + + # And add new one: + $exp .= '|' . $name; + + $property->update_property_validexp($exp); + + print "{'success':true}"; +} + +################################### +### Create Child Helper Methods ### +################################### +sub add_category { + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new( {} ); + if ($id) { + my $product = Bugzilla::Testopia::Product->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $product } ) unless $product->canedit; + } + $category->{'product_id'} = $id; + $category->{'name'} = 'New category ' . $category->new_category_count; + + my $new_cid = $category->store(); + + my $category_json = { + text => $category->{'name'}, + id => $new_cid, + type => 'category', + leaf => JSON::false, + cls => 'category' + }; + + my $json = new JSON; + print "{success: true, env_object: "; + print $json->encode($category_json); + print "}"; + +} + +sub add_element { + my ( $id, $ischild ) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new( {} ); + + # If we are adding this element as a child, $id is the parent element's id + if ($ischild) { + my $parent = Bugzilla::Testopia::Environment::Element->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $parent } ) unless $parent->canedit; + $element->{'env_category_id'} = $parent->env_category_id; + } + + # Otherwise $id is the catagory id + else { + my $parent = Bugzilla::Testopia::Environment::Category->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $parent } ) unless $parent->canedit; + $element->{'env_category_id'} = $id; + } + $element->{'name'} = "New element " . $element->new_element_count; + $element->{'parent_id'} = $ischild ? $id : 0; + $element->{'isprivate'} = 0; + + my $new_eid = $element->store(); + + my $element_json = { + text => $element->{'name'}, + id => $new_eid, + type => 'element', + leaf => JSON::false, + cls => 'element' + }; + + my $json = new JSON; + print "{success: true, env_object: "; + print $json->encode($element_json); + print "}"; +} + +sub add_property { + my ($id) = (@_); + + #add new property to element with id=$id + my $property = Bugzilla::Testopia::Environment::Property->new( {} ); + + my $parent = Bugzilla::Testopia::Environment::Element->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $parent } ) unless $parent->canedit; + + $property->{'element_id'} = $id; + $property->{'name'} = "New property " . $property->new_property_count; + $property->{'validexp'} = ""; + + my $new_pid = $property->store(); + + my $property_json = { + text => $property->{'name'}, + id => $new_pid, + type => 'property', + leaf => JSON::false, + cls => 'property', + draggable => JSON::false, + }; + + my $json = new JSON; + print "{success: true, env_object: "; + print $json->encode($property_json); + print "}"; +} + +sub add_validexp { + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + ThrowUserError( "testopia-read-only", { 'object' => $property } ) unless $property->canedit; + + my $exp = $property->validexp; + $exp + ? $property->update_property_validexp( $exp . "|New value" ) + : $property->update_property_validexp("New value"); + + my $value = { + text => "New value", + id => $id, + type => "value", + cls => "validexp", + draggable => JSON::false, + }; + + my $json = new JSON; + print "{success: true, env_object: "; + print $json->encode($value); + print "}"; +} + +############################# +### Delete Helper Methods ### +############################# +sub delete_category { + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + + ThrowUserError( "testopia-read-only", { 'object' => $category } ) unless $category->candelete; + ThrowUserError('testopia-element-in-use') if ( $category->is_mapped() ); + + $category->obliterate; + + print "{success: true}"; +} + +sub delete_element { + my ($id) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + + ThrowUserError( "testopia-read-only", { 'object' => $element } ) unless $element->candelete; + ThrowUserError('testopia-element-in-use') if ( $element->is_mapped() ); + + $element->obliterate; + + print "{success: true}"; +} + +sub delete_property { + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + + ThrowUserError( "testopia-read-only", { 'object' => $property } ) unless $property->candelete; + ThrowUserError('testopia-element-in-use') if ( $property->is_mapped() ); + + $property->obliterate; + + print "{success: true}"; +} + +sub delete_validexp { + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + + ThrowUserError( "testopia-read-only", { 'object' => $property } ) unless $property->candelete; + + my %values; + foreach my $v ( split /\|/, $property->validexp ) { + $values{$v} = 1; + } + + delete $values{$cgi->param("value")}; + my $exp = join( "|", keys %values ); + + $property->update_property_validexp($exp); + print "{success: true}"; +} diff --git a/tr_export_environment.cgi b/tr_export_environment.cgi new file mode 100755 index 0000000..be37af3 --- /dev/null +++ b/tr_export_environment.cgi @@ -0,0 +1,67 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Garrett Braden + +# This script expects an env_id in the query string pointing to the +# environment that you want to export to XML and displays the XML in +# a html textarea. + + +=head1 NAME + +tr_export_environment.cgi + +=head1 DESCRIPTION + +Exports an Environment by env_id from the database to XML and displays the XML in +a html textarea. + +=cut + +#************************************************** Uses ****************************************************# +use strict; +use CGI; +use lib qw(. lib); +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Xml; + +#************************************ Variable Declarations/Initialization **********************************# +my $vars = {}; +Bugzilla->login(LOGIN_REQUIRED); +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +my $env_id = $cgi->param('env_id'); + +#********************************************* UI Logic ************************************************# +print $cgi->header; +my $env = Bugzilla::Testopia::Environment->new($env_id); +ThrowUserError("testopia-read-only", {'object' => $env}) unless $env->canview; +my $xml = Bugzilla::Testopia::Environment::Xml->export($env_id); +if (!defined($xml)) { + $vars->{'tr_error'} .= "Exporting XML Environment Failed. Please try again.
"; +} +$vars->{'xml'} = $xml; +$template->process("testopia/environment/export.xml.tmpl", $vars) || print $template->error(); diff --git a/tr_history.cgi b/tr_history.cgi new file mode 100755 index 0000000..71887a6 --- /dev/null +++ b/tr_history.cgi @@ -0,0 +1,98 @@ +#!/usr/bin/perl -wT + +use strict; + +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Constants; +use JSON; + +local our $vars = {}; +local our $template = Bugzilla->template; +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $action = $cgi->param("action"); + +print "Location: tr_show_product.cgi\n\n" unless $action; + +print $cgi->header; + +my $obj; +if ($cgi->param('object') eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($cgi->param('object_id')); +} +elsif ($cgi->param('object') eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($cgi->param('object_id')); +} +elsif ($cgi->param('object') eq 'run'){ + $obj = Bugzilla::Testopia::TestRun->new($cgi->param('object_id')); +} +else{ + ThrowUserError("testopia-unknown-type", {'object' => $obj}); +} +ThrowUserError("testopia-permission-denied", {'object' => $obj}) unless $obj->canview; + +my $type = $cgi->param("type"); +my $id = trim($cgi->param("id")); + +if ($action eq 'diff') +{ + if ($type eq 'plan') + { + print $cgi->header; + + my $plan = Bugzilla::Testopia::TestPlan->new($id); + + ThrowUserError("testopia-permission-denied", {'object' => $plan}) unless $plan->canview; + + $vars->{'plan'} = $plan; + $vars->{'diff'} = $plan->diff_plan_doc($cgi->param('new'),$cgi->param('old')); + $vars->{'new'} = $cgi->param('new'); + $vars->{'old'} = $cgi->param('old'); + + $template->process("testopia/plan/history.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($type eq 'case') + { + + } +} + +elsif ($action eq 'showdoc'){ + if ($obj->type eq 'plan'){ + $vars->{'text'} = $obj->text($cgi->param('version')); + $template->process("testopia/plan/history.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($obj->type eq 'case'){ + $vars->{'text'} = $obj->text($cgi->param('version')); + $template->process("testopia/case/history.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } +} + +elsif ($action eq 'show'){ + my $json = new JSON; + print "{list:"; + print $json->encode($obj->history); + print "}"; + +} + +elsif ($action eq 'getdocversions'){ + my $json = new JSON; + print "{list:"; + print $json->encode($obj->get_text_versions); + print "}"; +} + diff --git a/tr_import_environment.cgi b/tr_import_environment.cgi new file mode 100755 index 0000000..44bf533 --- /dev/null +++ b/tr_import_environment.cgi @@ -0,0 +1,346 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Garrett Braden + +# This script reads in an environment in xml and persists it to the database. + + +=head1 NAME + +tr_import_environment.cgi - Imports an environment from a XML file into the database. + +=head1 DESCRIPTION + +Administrator's and testopia users can import an environment from XML into the database. +A tool is being designed to automatically scan a system and create such an XML file to +import. + +=cut + +#************************************************** Uses ****************************************************# +use strict; +use CGI; +use lib qw(. lib); +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Xml; +use Data::Dumper; + + +#************************************ Variable Declarations/Initialization **********************************# +Bugzilla->login(LOGIN_REQUIRED); +local our $cgi = Bugzilla->cgi; +local our $template = Bugzilla->template; +local our $vars = {}; +local our $upload_dir = "testopia/temp"; +local our $env_filename = $cgi->param('env_file'); +local our $env_fh = $cgi->upload('env_file'); +local our $action = $cgi->param('action') || ''; +local our $xml = $cgi->param('xml'); +local our $submit = $cgi->param('submit'); +local our $environment = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); +local our $user_can_edit = $environment->canedit; +#my $user_can_edit = 0; # Remove on production <- used to toggle user's rights. +our $message = ''; +$CGI::POST_MAX = 1024 * 500; # max file size 500K + +#***************************************** UI Logic ************************************************************# +print $cgi->header; +# Make sure the file isn't too big. +if (!$env_filename && $cgi->cgi_error()) { + $vars->{'tr_error'} .= "File size cannot exceed 500K.
"; +} +# Upload the file and read it into a string if it's been posted. +if ($action eq 'import' && $env_filename) { + trick_taint($env_filename); + # Continue only if import exits without errors, otherwise display the errors. + if (slurp_env_file() && $xml && import()) { + # Check for new data + if (check_new_items()) { + if (write_env_file()) { + # If there's new data and the user can edit have them approve the changes. + if ($user_can_edit) { + admin_approve(); + } + else { + $vars->{'tr_error'} .= "Please contact an admin who can import the above mentioned non-existing data.
"; + } + } + } + # If there's no new Categories, Elements, or Properties then store the environment. + else { + store_environment(); # Stores the new Environment values based on existing Categories, Elements, Properties, and Valid Expressions. + } + } +} +# If the admin form posted +elsif ($action eq 'admin' && $xml) { + if ($user_can_edit) { + my $file = "$upload_dir/$xml"; + trick_taint($file); + # If the admin clicked the 'Add Now' and not the 'Cancel' button + if ($submit eq 'Add Now') { + if (read_env_file($file)) { + import("admin"); # Creates Bugzilla::Testopia::Environment::Xml object and store's it to the database. + } + } + else { + $vars->{'tr_error'} .= "Import XML Environment Cancelled.
"; + delete_env_file($file); + } + } + else { + $vars->{'tr_error'} .= "Error reading from file. Please try again.
If the problem persists please contact the site administrator.
"; + } +} +display(); +#*********************************************** EXISTS HERE *************************************************# + + + + +#************************************* Sub Routine Deffinitions Below ****************************************# + + +=head2 Create XML document + +=head2 DESCRIPTION + +Creates the Environment XML Document and node lists. + +=cut + +sub import { + # If the user is an admin and has approved to add the values pass 1 to parse(). + my $admin = @_; + $environment = Bugzilla::Testopia::Environment::Xml->new($xml, $admin); + if ($environment->{'error'}) { + $vars->{'tr_error'} .= $environment->{'error'}; + return 0; + } + return 1; +} + + +=head2 Checking Exists + +=head2 DESCRIPTION + +Checking if the Environment, Elements, Categories, and Properties already exist or not. + +=cut + +sub check_new_items { + return $environment->check_new_items(); +} + + +=head2 Admin Approve + +=head2 Description + +Prepares the $vars being passed to the template to display the +Admin Approval Form first before importing an environment with +new data. + +=cut + +sub admin_approve { + $vars->{'action'} = "admin"; + $vars->{'xml'} = $xml; +} + + +=head2 Store the Environment + +=head2 Description + +Stores the Environment based on existing Categories, Elements, Properties, and selectable values. + +=cut + +sub store_environment { + $environment->store(); +} + + +=head2 Upload and read the XML file + +=head2 Description + +Uploads and reads the XML Environment file and prepares it into an array for parsing. + +=cut + +sub slurp_env_file { + my $untainted_filename; + my $post_max = $CGI::POST_MAX; + if (!$env_filename) { + $vars->{'tr_error'} .= "Please upload an Environment XML file.
"; + return 0; + } + # untaint $env_file + if ($env_filename =~ /^([-\@:\/\\\w.]+)$/) { + $untainted_filename = $1; + } + else { + $vars->{'tr_error'} .= "The filename must contain numbers and letters only. Please try again.
"; + return 0; + } + if ($untainted_filename =~ m/\.\./) { + $vars->{'tr_error'} .= "The filename cannot conain the sequence '..' Please try again.
"; + return 0; + } + my $num_bytes = $CGI::POST_MAX; + my ($totalbytes, $byteswritten, $buffer); + while ($byteswritten = read($env_fh, $buffer, $num_bytes)) { + $xml .= $buffer; + $totalbytes += $num_bytes; + } + if (!($byteswritten && $totalbytes)) { + $vars->{'error'} .= "Problem uploading file " . $env_filename . ".
" + } + if (!$xml) { + $vars->{'tr_error'} .= "File is empty. Please upload a valid Environment XML file.
"; + return 0; + } + if (length($xml) > $post_max) { + $vars->{'tr_error'} .= "File uploaded was too large. Please make sure the file size is no bigger than 500 KB.
"; + return 0; + } + return 1; +} + + +=head2 Write the Environment XML File to the Server + +=head Description + +Writes the environment XML File to the $upload_dir directory where it will be read in later by the admin to be approved. Once approved it's deleted. + +=cut + +sub write_env_file { + # If a writable $upload_dir exists, log error details there. + if (-w "$upload_dir") { + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + my $filename = $env_filename; + $filename =~ s/(.xml)//; + $filename .= "_" . $timestamp; + $filename =~ s/\:/./g; + $filename =~ s/[\: -]/_/g; + $filename .= ".xml"; + my $untainted_filename; + if (!$filename) { + $vars->{'tr_error'} .= "Please upload an Environment XML file.
"; + return 0; + } + # untaint $env_file + if ($filename =~ /^([-\@:\/\\\w.]+)$/) { + $untainted_filename = $1; + } + else { + $vars->{'tr_error'} .= "The filename must contain numbers and letters only. Please try again.
"; + return 0; + } + if ($untainted_filename =~ m/\.\./) { + $vars->{'tr_error'} .= "The filename cannot conain the sequence '..' Please try again.
"; + return 0; + } + open (my $fh, ">$upload_dir/$filename") || die "PROBLEM WRITING FILE.
"; + print $fh $xml; + close $fh; + $xml = $filename; + } + else { + $vars->{'tr_error'} .= "Unable to write temporary environment file. Please try again.
If the problem persists please contact the site administrator.
"; + return 0; + } + return 1; +} + + +=head2 Read the uploaded Environment XML file + +=head2 Description + +Uploads and reads the XML Environment file and prepares it into an array for parsing. + +=cut + +sub read_env_file { + my ($file) = @_; + $xml = ''; + open (my $fh, "<$file") || do { + $vars->{'tr_error'} .= "Unable to read temporary environment file. Please to try again.
If the problem persists please contact the site administrator.
"; + return 0; + }; + binmode($fh); + while (<$fh>) { + $xml .= $_; + } + close $fh; + if (!$xml) { + $vars->{'tr_error'} .= "File is empty. Please upload a valid Environment XML file.
"; + return 0; + } + if (length($xml) > $CGI::POST_MAX) { + $vars->{'tr_error'} .= "File uploaded was too large. Please make sure the file size is no bigger than 500 KB.
"; + return 0; + } + return delete_env_file($file); +} + + +=head2 delete_env_file + +=head2 Description + +Deletes the temporary environment file. + +=cut + +sub delete_env_file { + my ($file) = @_; + if (!unlink($file)) { + $vars->{'tr_error'} .= "Unable to delete temporary environment file.
If the problem persists please contact the site administrator.
"; + return 0; + } + return 1; +} + + +=head2 Display + +=head2 Description + +Displays the Import Environment template + +=cut + +sub display { + $vars->{'tr_message'} .= $message . $environment->{'message'}; + $template->process("testopia/environment/import.xml.tmpl", $vars) || print $template->error(); +} diff --git a/tr_importer.cgi b/tr_importer.cgi new file mode 100644 index 0000000..6d33753 --- /dev/null +++ b/tr_importer.cgi @@ -0,0 +1,239 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Product; +use Bugzilla::Token; + +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::Xml; + +use XML::Twig; +use Text::CSV; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; +my $dbh = Bugzilla->dbh; + +# Re-bless CGI's filehandle lite as a real IO::Handle so that Text::CSV +# knows what to do with it +@Fh::ISA= qw( IO::Handle ); + +Bugzilla->login(LOGIN_REQUIRED); + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $ctype = $cgi->param('ctype') || ''; + +Bugzilla->error_mode(ERROR_MODE_AJAX) if $ctype eq 'json'; + +if ($action eq 'upload') { + my $token = trim($cgi->param('token')); + if ($token) { + my ($creator_id, $date, $old_file) = Bugzilla::Token::GetTokenData($token); + unless ($creator_id + && ($creator_id == Bugzilla->user->id) + && ($old_file =~ "^importtests:")) + { + # The token is invalid. + ThrowUserError('token_inexistent'); + } + $old_file =~ s/^importtests://; + # Must have hit refresh on the form. + ThrowUserError('import_repeat') if ($old_file); + + } + defined $cgi->upload('data') || ThrowUserError("file_not_specified"); + my $type = $cgi->uploadInfo($cgi->param("data"))->{'Content-Type'}; + + # IE Sends application/octet-stream + if ($type =~ /application/){ + if ($cgi->param('data') =~ /\.csv$/){ + $type = "text/csv"; + } + elsif ($cgi->param('data') =~ /\.xml$/){ + $type = "text/xml"; + } + } + + ThrowUserError('invalid_import_type', {type => $type}) unless $type =~ /text\/(plain|xml|csv|x-comma-separated-values)/; + + if ($type eq 'text/xml'){ + my $fh = $cgi->upload('data'); + my $data; + # enable 'slurp' mode + local $/; + + $data = <$fh>; + # Limit to 1 MB. Anything larger will take way too long to parse. + ThrowUserError("file_too_large", { filesize => sprintf("%.0f", length($data)/1024) }) if length($data) > 1048576; + + my $testopiaXml = Bugzilla::Testopia::Xml->new(); + my $case_ids = $testopiaXml->parse($data); + + if ($ctype eq 'json'){ + print '{success: true}'; + exit; + } + else{ + $vars->{'cases'} = join(',', @$case_ids); + $template->process("testopia/import/importer.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + } + + } + else { + # Define the fields of the CSV file. The file must be in this order. + my @fields = qw( + product + plans + summary + author_id + default_tester_id + case_status_id + priority_id + category_id + components + requirement + estimated_time + isautomated + script + arguments + alias + tags + bugs + dependson + blocks + runs + setup + breakdown + action + effect + ); + + my $csv = Text::CSV->new({binary => 1, eol => $/}); + my $fh = $cgi->upload('data'); + + $csv->column_names(\@fields); + my @rows; + my @validated; + while(my $row = $csv->getline_hr($fh)){ + # print Data::Dumper::Dumper($row); + push @rows, $row; + } + + if (! $csv->eof()){ + ThrowUserError('csv_parse_failure', {row => scalar @rows + 1}); + } + # Validate all the fields ahead of time to ensure that we have an atomic upload + foreach my $row (@rows){ + next if ($row->{'plans'} =~ /Plans/); + my $product = Bugzilla::Product::check_product($row->{'product'}); + delete $row->{'product'}; + + my $import_plan = $cgi->param('plan_id'); + if (detaint_natural($import_plan)){ + $row->{'plans'} .= ',' if $row->{'plans'}; + $row->{'plans'} .= "$import_plan"; + } + my @plans; + foreach my $id (split(/[\s,]+/, $row->{'plans'})){ + my $plan = Bugzilla::Testopia::TestPlan->new($id); + ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Plan'}) unless $plan; + ThrowUserError("testopia-create-denied", {'object' => 'Test Case', 'plan' => $plan}) unless $plan->canedit; + push @plans, $plan; + } + $row->{'plans'} = \@plans; + $row->{'author_id'} ||= Bugzilla->user->id; + my $category = $row->{'category_id'}; + trick_taint($category); + if (trim($category) =~ /^\d+$/){ + $row->{'category_id'} = Bugzilla::Testopia::Util::validate_selection($row->{'category_id'}, 'category_id', 'test_case_categories'); + } + else { + $category = check_case_category($category, $product); + ThrowUserError("invalid-test-id-non-existent", {'id' => $row->{'category_id'}, 'type' => 'category'}) unless $category; + $row->{'category_id'} = $category; + } + my @comps; + foreach my $comp (split(/,+/,$row->{'components'})){ + if (trim($comp) =~ /^\d+$/){ + push @comps, $comp; + } + else { + push @comps, {component => trim($comp), product => $product->name}; + } + } + $row->{'components'} = \@comps; + $row->{'isautomated'} = $row->{'isautomated'} ? 1 : 0; + + # print Data::Dumper::Dumper($row); + my $case = Bugzilla::Testopia::TestCase->new({}); + + $case->check_required_create_fields($row); + $case->run_create_validators($row); + push @validated, $row; + } + + # OK, if we are here, all the fields passed validation. Time to create + my @case_ids; + foreach my $row (@validated){ + my $case = Bugzilla::Testopia::TestCase->create($row); + push @case_ids, $case->id; + } + if ($ctype eq 'json'){ + print '{success: true}'; + exit; + } + else{ + $vars->{'cases'} = join(',',@case_ids); + $template->process("testopia/import/importer.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + } + } + if ($token) { + trick_taint($token); + my $filename = $cgi->param('data'); + trick_taint($filename); + $dbh->do('UPDATE tokens SET eventdata = ? WHERE token = ?', undef, + ("importtests:$filename", $token)); + } +} + +else { + $vars->{'token'} = issue_session_token('importtests:'); + $template->process("testopia/import/importer.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + +} + diff --git a/tr_importxml.pl b/tr_importxml.pl new file mode 100644 index 0000000..b230b62 --- /dev/null +++ b/tr_importxml.pl @@ -0,0 +1,167 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Dawn Endico +# Gregary Hendricks +# Vance Baarda + + +# This script reads in xml bug data from standard input and inserts +# a new bug into bugzilla. Everything before the beginning usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE); + +my $debug = 0; +my $help = 0; +my $login = undef; +my $pass = undef; + +my $result = GetOptions("verbose|debug+" => \$debug, + "help|?" => \$help, + "login=s" => \$login, + "pass=s" => \$pass); + +pod2usage(0) if $help; + +use constant DEBUG_LEVEL => 2; +use constant ERR_LEVEL => 1; + +sub Debug { + return unless ($debug); + my ($message, $level) = (@_); + print STDERR "ERR: ". $message ."\n" if ($level == ERR_LEVEL); + print STDERR "$message\n" if (($debug == $level) && ($level == DEBUG_LEVEL)); +} + +Debug("Reading xml", DEBUG_LEVEL); + +my $xml; +my $filename; +if ( $#ARGV == -1 ) +{ + # Read STDIN in slurp mode. VERY dangerous, but we live on the wild side ;-) + local($/); + $xml = <>; +} +elsif ( $#ARGV == 0 ) +{ + $filename = $ARGV[0]; +} +else +{ + pod2usage(0); +} + +# Log in if credentials are provided. +if (defined $login) +{ + Debug("Logging in as '$login'", DEBUG_LEVEL); + + # Make sure no user is logged in + Bugzilla->logout(); + + my $cgi = Bugzilla->cgi(); + $cgi->param("Bugzilla_login", $login); + $cgi->param("Bugzilla_password", $pass); + + Bugzilla->login(); +} + +Debug("Parsing tree", DEBUG_LEVEL); + +my $testopiaXml = Bugzilla::Testopia::Xml->new(); +$testopiaXml->parse($xml,$filename); + +exit 0; + +__END__ + +=head1 NAME + +tr_importxml - Import Testopia data from xml. + +=head1 SYNOPSIS + + tr_importxml.pl [options] [file] + + Options: + -? --help Brief help message. + -v --verbose Print error and debug information. + Multiple -v options increase verbosity. + --login Login ID (email address) + --pass Password + + With no file read standard input. + +=head1 OPTIONS + +=over 8 + +=item B<-?> + + Print a brief help message and exits. + +=item B<-v> + + Print error and debug information. Multiple -v increases verbosity + +=back + +=head1 DESCRIPTION + + This script is used import Test Plans and Test Cases into Testopia. + + +=cut diff --git a/tr_insanity.pl b/tr_insanity.pl new file mode 100644 index 0000000..e6efb35 --- /dev/null +++ b/tr_insanity.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Portions lifted from bugzilla's sanitycheck.cgi +# +# Contributor(s): Greg Hendricks + +use strict; + +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::User; + +my $dbh = Bugzilla->dbh; + + +# Removed orphaned records + +$dbh->bz_lock_tables('test_plan_permissions WRITE', 'test_plan_permissions_regexp WRITE', + 'test_plans READ'); + +foreach my $pair ('test_plan_permissions/plan_id', 'test_plan_permissions_regexp/plan_id') { + + my ($table, $field) = split('/', $pair); + + my $ids = $dbh->selectcol_arrayref( + "SELECT $table.$field FROM $table + LEFT JOIN test_plans ON $table.$field = test_plans.plan_id + WHERE test_plans.plan_id IS NULL"); + + if (scalar(@$ids)) { + $dbh->do("DELETE FROM $table WHERE $field IN (" . join(',', @$ids) . ")"); + } +} + +$dbh->bz_unlock_tables(); diff --git a/tr_list_caseruns.cgi b/tr_list_caseruns.cgi new file mode 100755 index 0000000..200743f --- /dev/null +++ b/tr_list_caseruns.cgi @@ -0,0 +1,148 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Bug; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; +use JSON; + +my $vars = {}; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +my $format = $template->get_format("testopia/caserun/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); + +print $cgi->header; + +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + +my $action = $cgi->param('action') || ''; + +if ($action eq 'update'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + my @caseruns; + my @uneditable; + my $assignee_id; + my $status_id; + my $note = $cgi->param('note'); + + trick_taint($note) if $note; + + if ($cgi->param('applyall') eq 'true'){ + my $run = Bugzilla::Testopia::TestRun->new($cgi->param('run_id')); + exit if $run->stop_date; + @caseruns = @{$run->current_caseruns()} if $run->canedit; + + } + else{ + foreach my $id (split(',', $cgi->param('ids'))){ + my $caserun = Bugzilla::Testopia::TestCaseRun->new($id); + if ($caserun->canedit){ + push @caseruns, $caserun; + } + else { + push @uneditable, $caserun->case_id; + } + } + } + + $status_id = $cgi->param('status_id') if $cgi->param('status_id'); + $assignee_id = login_to_id(trim($cgi->param('assignee')),'THROW_ERROR') if $cgi->param('assignee'); + # If setting to running they can choose to make themselves the assignee. + $assignee_id = Bugzilla->user->id if $cgi->param('reassign'); + detaint_natural($status_id); + + foreach my $cr (@caseruns){ + next if $cr->run->stop_date; + $cr = $cr->switch($cgi->param('build_id')) if $cgi->param('build_id'); + $cr = $cr->switch($cr->build->id, $cgi->param('env_id')) if $cgi->param('env_id'); + $cr->set_status($status_id, $cgi->param('update_bug') eq 'true' ? 1 : 0) if $status_id; + $cr->set_assignee($assignee_id) if $assignee_id; + $cr->append_note($note); + } + + ThrowUserError('testopia-update-failed', {'object' => 'case-run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + exit unless scalar @caseruns; + + my $run = $caseruns[0]->run; + $vars->{'passed'} = $run->case_run_count(PASSED) / $run->case_run_count; + $vars->{'failed'} = $run->case_run_count(FAILED) / $run->case_run_count; + $vars->{'blocked'} = $run->case_run_count(BLOCKED) / $run->case_run_count; + $vars->{'complete'} = $run->percent_complete() . '%'; + $vars->{'success'} = JSON::true ; + + print to_json($vars); +} + +elsif ($action eq 'delete'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + my @case_ids; + if ($cgi->param('ids')){ + @case_ids = $cgi->param('ids'); + } + else { + @case_ids = split(",", $cgi->param('caserun_ids')); + } + my @uneditable; + foreach my $id (@case_ids){ + my $case = Bugzilla::Testopia::TestCaseRun->new($id); + unless ($case->candelete){ + push @uneditable, $case->id; + next; + } + + $case->obliterate($cgi->param('single')); + } + + ThrowUserError('testopia-update-failed', {'object' => 'case-run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; +} + +else { + $vars->{'qname'} = $cgi->param('qname') if $cgi->param('qname'); + + # Take the search from the URL params and convert it to SQL + $cgi->param('current_tab', 'case_run'); + $cgi->param('distinct', '1'); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_list_caseruns.cgi', $cgi, undef, $search->query); + + print $cgi->header; + $vars->{'json'} = $table->to_ext_json; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); +} diff --git a/tr_list_cases.cgi b/tr_list_cases.cgi new file mode 100755 index 0000000..1a090bd --- /dev/null +++ b/tr_list_cases.cgi @@ -0,0 +1,336 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Jeff Dayley + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; + +my $vars = {}; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +# Determine the format in which the user would like to receive the output. +# Uses the default format if the user did not specify an output format; +# otherwise validates the user's choice against the list of available formats. +my $format = $template->get_format("testopia/case/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); +my $action = $cgi->param('action') || ''; + +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + +############### +### Actions ### +############### +if ($action eq 'update'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + my @case_ids = split(',', $cgi->param('ids')); + ThrowUserError('testopia-none-selected', {'object' => 'case'}) unless (scalar @case_ids); + + my @uneditable; + my @runs; + my @bugs; + my @components; + + foreach my $runid (split(/[\s,]+/, $cgi->param('addruns'))){ + validate_test_id($runid, 'run'); + push @runs, Bugzilla::Testopia::TestRun->new($runid); + } + + foreach my $bugid (split(/[\s,]+/, $cgi->param('bugs'))){ + ValidateBugID($bugid); + push @bugs, $bugid; + } + + my @comps = $cgi->param("components"); + my (@addcomponents,@remcomponents); + foreach my $id (@comps){ + detaint_natural($id); + validate_selection($id, 'id', 'components'); + if ($cgi->param('comp_action') eq 'add'){ + push @addcomponents, $id; + } + else { + push @remcomponents, $id; + } + } + + foreach my $p (@case_ids){ + my $case = Bugzilla::Testopia::TestCase->new($p); + next unless $case; + + unless ($case->canedit){ + push @uneditable, $case; + next; + } + + $case->set_requirement($cgi->param('requirement')) if $cgi->param('requirement'); + $case->set_case_status($cgi->param('status')) if $cgi->param('status'); + $case->set_priority($cgi->param('priority')) if $cgi->param('priority'); + $case->set_isautomated($cgi->param('isautomated') eq 'on' ? 1 : 0) if $cgi->param('isautomated'); + $case->set_script($cgi->param('script')) if $cgi->param('script'); + $case->set_arguments($cgi->param('arguments')) if $cgi->param('arguments'); + $case->set_category($cgi->param('category')) if $cgi->param('category'); + $case->set_default_tester($cgi->param('tester')) if $cgi->param('tester'); + + $case->update(); + + $case->add_component($_) foreach (@addcomponents); + $case->remove_component($_) foreach (@remcomponents); + + # Add to runs + foreach my $run (@runs){ + $run->add_case_run($case->id, $case->sortkey) if $run->canedit; + } + + $case->attach_bugs(\@bugs) if $cgi->param('bugs_action') eq 'add'; + $case->detach_bugs(\@bugs) if $cgi->param('bugs_action') eq 'remove'; + } + ThrowUserError('testopia-update-failed', {'object' => 'plan', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; + +} + +elsif ($action eq 'clone'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + + my @case_ids = split(',', $cgi->param('ids')); + ThrowUserError('testopia-none-selected', {'object' => 'case'}) unless (scalar @case_ids); + + my %planseen; + foreach my $planid (split(",", $cgi->param('plan_ids'))){ + validate_test_id($planid, 'plan'); + my $plan = Bugzilla::Testopia::TestPlan->new($planid); + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + $planseen{$planid} = 1; + } + + ThrowUserError('missing-plans-list') unless scalar keys %planseen; + + my $product = Bugzilla::Testopia::Product->new($cgi->param('product_id')); + ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $cgi->param('product_id')}) unless $product; + + if ($cgi->param('copy_category')){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + } + + my @newcases; + foreach my $id (@case_ids){ + my $case = Bugzilla::Testopia::TestCase->new($id); + next unless $case; + next unless ($case->canview); + + # Clone + if ($cgi->param('copy_cases')){ + # Copy test cases creating new ones + my $case_author = $cgi->param('keep_author') ? $case->author->id : Bugzilla->user->id; + my $case_tester = $cgi->param('keep_tester') ? $case->default_tester->id : Bugzilla->user->id; + my $category; + if ($cgi->param('copy_category')){ + my $category_id = check_case_category($case->category->name, $product); + if (! $category_id){ + $category = Bugzilla::Testopia::Category->create({ + product_id => $product->id, + name => $case->category->name, + description => $case->category->description, + }); + } + else { + $category = Bugzilla::Testopia::Category->new($category_id); + } + } + else { + if ($product->id == $case->category->product_id){ + $category = $case->category; + } + else{ + my @categories = @{$product->categories}; + if (scalar @categories < 1){ + $category = Bugzilla::Testopia::Category->create({ + product_id => $product->id, + name => '--default--', + description => 'Default product category for test cases', + }); + + } + else{ + $category = $categories[0]; + } + } + } + + my $caseid = $case->copy($case_author, $case_tester, $cgi->param('copy_doc') eq 'on' ? 1 : 0, $category->id); + my $newcase = Bugzilla::Testopia::TestCase->new($caseid); + push @newcases, $newcase->id; + + foreach my $plan_id (keys %planseen){ + $newcase->link_plan($plan_id, $caseid); + } + + if ($cgi->param('copy_attachments')){ + foreach my $att (@{$case->attachments}){ + $att->link_case($newcase->id); + } + } + if ($cgi->param('copy_tags')){ + foreach my $tag (@{$case->tags}){ + $newcase->add_tag($tag->name); + } + } + if ($cgi->param('copy_comps')){ + foreach my $comp (@{$case->components}){ + $newcase->add_component($comp->{'id'}); + } + } + } + # Just create a link + else { + foreach my $plan_id (keys %planseen){ + $case->link_plan($plan_id); + } + } + } + print "{'success': true, 'tclist': [". join(", ", @newcases) ."]}"; +} + +elsif ($action eq 'delete'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + + my @case_ids = split(",", $cgi->param('case_ids')); + my @uneditable; + foreach my $id (@case_ids){ + my $case = Bugzilla::Testopia::TestCase->new($id); + unless ($case->candelete){ + push @uneditable, $case; + next; + } + + $case->obliterate; + } + + ThrowUserError('testopia-update-failed', {'object' => 'case', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; +} + +elsif ($action eq 'unlink'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + my $plan_id = $cgi->param('plan_id'); + validate_test_id($plan_id, 'plan'); + foreach my $id (split(",", $cgi->param('case_ids'))){ + my $case = Bugzilla::Testopia::TestCase->new($id); + if (scalar @{$case->plans} == 1){ + ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->candelete); + $case->obliterate(); + } + else { + ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->can_unlink_plan($plan_id)); + ThrowUserError('testopia-case-unlink-failure') unless $case->unlink_plan($plan_id); + } + } + print "{'success': true}"; +} + +elsif ($action eq 'update_bugs'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + + my @ids = split(",", $cgi->param('ids')); + my @objs; + foreach my $id (split(",", $cgi->param('ids'))){ + if ($cgi->param('type') eq 'case'){ + my $case = Bugzilla::Testopia::TestCase->new($id); + if ($cgi->param('bug_action') eq 'attach'){ + $case->attach_bug($cgi->param('bugs')) if $case->canedit; + } + else { + $case->detach_bug($cgi->param('bugs')) if $case->canedit; + } + } elsif($cgi->param('type') eq 'caserun'){ + my $caserun = Bugzilla::Testopia::TestCaseRun->new($id); + if ($cgi->param('bug_action') eq 'attach'){ + $caserun->attach_bug($cgi->param('bugs'), $id) if $caserun->canedit; + } + else { + $caserun->detach_bug($cgi->param('bugs')) if $caserun->canedit; + } + } + } +} + +else{ + $vars->{'qname'} = $cgi->param('qname') if $cgi->param('qname'); + + $cgi->param('current_tab', 'case'); + $cgi->param('distinct', '1'); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case', 'tr_list_cases.cgi', $cgi, undef, $search->query); + + if ($cgi->param('ctype') eq 'json'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + $vars->{'json'} = $table->to_ext_json; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); + exit; + } + + my @time = localtime(time()); + my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; + my $filename = "testcases-$date.$format->{extension}"; + + my $disp = "inline"; + # We set CSV files to be downloaded, as they are designed for importing + # into other programs. + if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + $vars->{'displaycolumns'} = \@Bugzilla::Testopia::Constants::TESTCASE_EXPORT; + } + + # Suggest a name for the bug list if the user wants to save it as a file. + print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); + +} \ No newline at end of file diff --git a/tr_list_environments.cgi b/tr_list_environments.cgi new file mode 100755 index 0000000..fc6c150 --- /dev/null +++ b/tr_list_environments.cgi @@ -0,0 +1,64 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; +use Bugzilla::Testopia::Constants; + +Bugzilla->login(LOGIN_REQUIRED); + + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +my $format = $template->get_format("testopia/case/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); +my $action = $cgi->param('action') || ''; + +$vars->{'qname'} = $cgi->param('qname') if $cgi->param('qname'); + +$cgi->param('current_tab', 'environment'); +$cgi->param('distinct', '1'); +my $search = Bugzilla::Testopia::Search->new($cgi); +my $table = Bugzilla::Testopia::Table->new('environment', 'tr_list_environments.cgi', $cgi, undef, $search->query); + +if ($cgi->param('ctype') eq 'json'){ + print $cgi->header; + $vars->{'json'} = $table->to_ext_json; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); +} +else{ + print "Location: tr_show_product.cgi?tab=environments\n\n"; +} diff --git a/tr_list_plans.cgi b/tr_list_plans.cgi new file mode 100755 index 0000000..ee478af --- /dev/null +++ b/tr_list_plans.cgi @@ -0,0 +1,88 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Jeff Dayley + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; + +my $vars = {}; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $format = $template->get_format("testopia/plan/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); + +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + +my $action = $cgi->param('action') || ''; + +if ($action eq 'update'){ + print $cgi->header; + my @plan_ids = split(',', $cgi->param('ids')); + + ThrowUserError('testopia-none-selected', {'object' => 'plan'}) unless (scalar @plan_ids); + + my $total = scalar @plan_ids; + my @uneditable; + foreach my $p (@plan_ids){ + my $plan = Bugzilla::Testopia::TestPlan->new($p); + next unless $plan; + + unless ($plan->canedit){ + push @uneditable, $p; + next; + } + + $plan->set_type($cgi->param('plan_type')) if $cgi->param('plan_type'); + $plan->update(); + + } + + ThrowUserError('testopia-update-failed', {'object' => 'plans', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; +} +else { + $vars->{'qname'} = $cgi->param('qname') if $cgi->param('qname'); + $cgi->param('current_tab', 'plan'); + $cgi->param('distinct', '1'); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('plan', 'tr_list_plans.cgi', $cgi, undef, $search->query); + + print $cgi->header; + $vars->{'json'} = $table->to_ext_json; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); +} diff --git a/tr_list_runs.cgi b/tr_list_runs.cgi new file mode 100755 index 0000000..9e1c9a7 --- /dev/null +++ b/tr_list_runs.cgi @@ -0,0 +1,201 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Jeff Dayley + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Constants; + +my $vars = {}; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +# Determine the format in which the user would like to receive the output. +# Uses the default format if the user did not specify an output format; +# otherwise validates the user's choice against the list of available formats. +my $format = $template->get_format("testopia/run/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); + +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + +my $action = $cgi->param('action') || ''; +if ($action eq 'update'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + print $cgi->header; + + my @run_ids = split(',', $cgi->param('ids')); + ThrowUserError('testopia-none-selected', {'object' => 'run'}) unless (scalar @run_ids); + + my @uneditable; + foreach my $p (@run_ids){ + my $run = Bugzilla::Testopia::TestRun->new($p); + next unless $run; + + unless ($run->canedit){ + push @uneditable, $run; + next; + } + + $run->set_manager($cgi->param('manager')) if $cgi->param('manager'); + $run->set_build($cgi->param('build')) if $cgi->param('build'); + $run->set_environment($cgi->param('environment')) if $cgi->param('environment'); + $run->set_target_pass($cgi->param('target_pass')) if exists $cgi->{'target_pass'}; + $run->set_target_completion($cgi->param('target_completion')) if exists $cgi->{'target_completion'}; + + $run->update(); + } + + ThrowUserError('testopia-update-failed', {'object' => 'run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; + +} + +elsif ($action eq 'clone'){ + print $cgi->header; + Bugzilla->error_mode(ERROR_MODE_AJAX); + + my @run_ids = split(',', $cgi->param('ids')); + ThrowUserError('testopia-none-selected', {'object' => 'run'}) unless (scalar @run_ids); + + my %planseen; + foreach my $planid (split(",", $cgi->param('plan_ids'))){ + validate_test_id($planid, 'plan'); + my $plan = Bugzilla::Testopia::TestPlan->new($planid); + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + $planseen{$planid} = 1; + } + + ThrowUserError('missing-plans-list') unless scalar keys %planseen; + + my $dbh = Bugzilla->dbh; + my $summary = $cgi->param('new_run_summary'); + my $build = $cgi->param('new_run_build'); + my $env = $cgi->param('new_run_environment'); + + trick_taint($summary) if $cgi->param('new_run_summary'); + detaint_natural($build) if $cgi->param('new_run_build'); + detaint_natural($env) if $cgi->param('new_run_environment'); + validate_test_id($build, 'build') if $cgi->param('new_run_build'); + validate_test_id($env, 'environment') if $cgi->param('new_run_environment'); + + my @newruns; + my @failures; + foreach my $run_id (@run_ids){ + my $run = Bugzilla::Testopia::TestRun->new($run_id); + next unless $run->canview; + + my $manager = $cgi->param('keep_run_manager') ? $run->manager->id : Bugzilla->user->id; + + $summary ||= $run->summary; + $build ||= $run->build->id; + $env ||= $run->environment->id; + + my @caseruns; + if ($cgi->param('copy_cases')){ + if ($cgi->param('case_list')){ + foreach my $id (split(",", $cgi->param('case_list'))){ + my $caserun = Bugzilla::Testopia::TestCaseRun->new($id); + ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless ($caserun->canview); + push @caseruns, $caserun; + } + } + else{ + $cgi->param('current_tab', 'case_run'); + $cgi->param('run_id', $run->id); + $cgi->param('viewall', 1); + $cgi->param('distinct', 1); + $cgi->delete('product_id'); + $cgi->delete('plan_ids'); + + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_list_caseruns.cgi', $cgi, undef, $search->query); + @caseruns = @{$table->list}; + } + } + + foreach my $plan_id (keys %planseen){ + my $newrun = Bugzilla::Testopia::TestRun->new($run->clone($summary, $manager, $plan_id, $build, $env)); + + if($cgi->param('copy_tags')){ + foreach my $tag (@{$run->tags}){ + $newrun->add_tag($tag->name); + } + } + + foreach my $cr (@caseruns){ + my $result = $newrun->add_case_run($cr->case_id, + $cgi->param('keep_indexes') ? $cr->sortkey : undef, + $cgi->param('keep_statuses') ? $cr->status_id : undef); + if ($result == 0){ + push @failures, $cr->case_id; + } + } + push @newruns, $newrun->id; + } + } + print "{'success': true, 'runlist': [". join(", ", @newruns) ."], 'failures': [". join(", ", @failures) ."]}"; +} + +elsif ($action eq 'delete'){ + print $cgi->header; + Bugzilla->error_mode(ERROR_MODE_AJAX); + my @run_ids = split(",", $cgi->param('run_ids')); + my @uneditable; + foreach my $id (@run_ids){ + my $run = Bugzilla::Testopia::TestRun->new($id); + unless ($run->candelete){ + push @uneditable, $run; + next; + } + + $run->obliterate; + } + + ThrowUserError('testopia-update-failed', {'object' => 'run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + print "{'success': true}"; +} + +else { + print $cgi->header; + $vars->{'qname'} = $cgi->param('qname') if $cgi->param('qname'); + $cgi->param('current_tab', 'run'); + $cgi->param('distinct', '1'); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('run', 'tr_list_runs.cgi', $cgi, undef, $search->query); + + $vars->{'json'} = $table->to_ext_json; + $template->process($format->{'template'}, $vars) + || ThrowTemplateError($template->error()); +} diff --git a/tr_new_case.cgi b/tr_new_case.cgi new file mode 100755 index 0000000..8d72bd9 --- /dev/null +++ b/tr_new_case.cgi @@ -0,0 +1,206 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; +use JSON; + +############################################################################### +# tr_new_case.cgi +# Presents a webform to the user for the creation of a new test case. +# +# INTERFACE: +# plan_id: list - list of plans that the newly created test case will +# be attached to. If no plan_id is found, the user will first +# be presented with a form to select a plan. +# +# action: undef - Present form for new case creation +# "Add" - Form has been submitted with case data. Create the test +# case. +# +################################################################################ + +my $vars = {}; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my @plan_id = split(',', $cgi->param('plan_id')); + +unless ($plan_id[0]){ + $vars->{'product'} = Bugzilla::Testopia::Product->new({'name' => $cgi->param('product')}) if ($cgi->param('product')); + $vars->{'bug_id'} = $cgi->param('bug'); + $vars->{'form_action'} = 'tr_new_case.cgi'; + $vars->{'type'} = "Case"; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} +my %seen; +my @plans; +my @plan_ids; +my @categories; + +# Weed out duplicates. +foreach my $entry (@plan_id){ + foreach my $id (split(/[\s,]+/, $entry)){ + detaint_natural($id); + validate_test_id($id, 'plan'); + $seen{$id} = 1; + } +} + +# Users need write permission on the plan in order to create a test case against +# that plan. See tr_plan_access.cgi +foreach my $id (keys %seen){ + my $plan = Bugzilla::Testopia::TestPlan->new($id); + ThrowUserError("testopia-create-denied", {'object' => 'Test Case', 'plan' => $plan}) unless $plan->canedit; + push @plan_ids, $id; + push @plans, $plan; + push @categories, @{$plan->product->categories}; +} + +# We need at least one category in the list. +ThrowUserError('testopia-create-category', {'plan' => $plans[0] }) if scalar @categories < 1; +if ($action eq 'add'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + my @comps = split(',', $cgi->param("components")); + my $case = Bugzilla::Testopia::TestCase->create({ + 'alias' => $cgi->param('alias') || '', + 'case_status_id' => $cgi->param('status') || '', + 'category_id' => $cgi->param('category') || '', + 'priority_id' => $cgi->param('priority') || '', + 'isautomated' => $cgi->param("isautomated") eq 'on' ? 1 : 0, + 'estimated_time' => $cgi->param("estimated_time") || '', + 'script' => $cgi->param("script") || '', + 'arguments' => $cgi->param("arguments") || '', + 'summary' => $cgi->param("summary") || '', + 'requirement' => $cgi->param("requirement") || '', + 'default_tester_id' => $cgi->param("tester") || '', + 'author_id' => Bugzilla->user->id || '', + 'action' => $cgi->param("tcaction") || '', + 'effect' => $cgi->param("tceffect") || '', + 'setup' => $cgi->param("tcsetup") || '', + 'breakdown' => $cgi->param("tcbreakdown") || '', + 'dependson' => $cgi->param("tcblocks") || '', + 'blocks' => $cgi->param("tcdependson") || '', + 'tags' => $cgi->param('addtags') || '', + 'runs' => $cgi->param('addruns') || '', + 'bugs' => $cgi->param('bugs') || '', + 'plans' => \@plans, + 'components' => \@comps, + + }); + + + my $err = JSON::false; + for (my $i=1; $i<5; $i++){ + next unless defined $cgi->upload("file$i"); + + my $fh = $cgi->upload("file$i"); + my $data; + # enable 'slurp' mode + local $/; + $data = <$fh>; + Bugzilla->error_mode(ERROR_MODE_DIE); + eval { + $data || ThrowUserError("zero_length_file"); + my $attachment = Bugzilla::Testopia::Attachment->create({ + case_id => $case->id, + submitter_id => Bugzilla->user->id, + description => $cgi->param("file_desc$i") || 'Attachment', + filename => $cgi->upload("file$i"), + mime_type => $cgi->uploadInfo($cgi->param("file$i"))->{'Content-Type'}, + contents => $data + }); + }; + if ($@){ + $err = JSON::true; + } + } + + print "{success: true, tc: '". $case->id ."', err: $err}"; +} + +#################### +### Display Form ### +#################### +else { + my $summary; + my $text; + if( $cgi->param('bug')){ + my $bug; + $bug = Bugzilla::Bug->new($cgi->param('bug'),Bugzilla->user->id); + + my $bug_id = $bug->bug_id; + my $description = '
' . wrap_comment(@{Bugzilla::Bug::GetComments($bug_id,'oldest_to_newest')}[0]->{'body'}) . '
'; + my $short_desc = $bug->short_desc; + + $summary = Bugzilla->params->{"bug-to-test-case-summary"}; + my $action = Bugzilla->params->{"bug-to-test-case-action"}; + my $effect = Bugzilla->params->{"bug-to-test-case-results"}; + + $summary =~ s/%id%/$bug_id/g; + $summary =~ s/%summary%/$short_desc/g; + + $action =~ s/%id%/$bug_id<\/a>/g; + $action =~ s/%description%/$description/g; + + $effect =~ s/%id%/$bug_id<\/a>/g; + + $text = {'action' => $action, 'effect' => $effect}; + + $vars->{'bugs'} = $bug->bug_id; + } + else { + $text = {'action' => Bugzilla->params->{"new-case-action-template"}, + 'effect' => Bugzilla->params->{"new-case-results-template"}}; + } + + my $case = Bugzilla::Testopia::TestCase->new( + {'plans' => join(',', @plan_ids), + 'category' => {name => '--default--'}, + 'summary' => $summary, + 'text' => $text, + }); + + $vars->{'tc'} = $case; + $vars->{'product_id'} = $plans[0]->product_id; + + $template->process("testopia/case/add.html.tmpl", $vars) || + ThrowTemplateError($template->error()); +} diff --git a/tr_new_environment.cgi b/tr_new_environment.cgi new file mode 100755 index 0000000..03e28bd --- /dev/null +++ b/tr_new_environment.cgi @@ -0,0 +1,88 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; +use Bugzilla::Testopia::Constants; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $vars = {}; +my $template = Bugzilla->template; + +print $cgi->header; + +ThrowUserError("testopia-create-denied", {'object' => 'Test Environment'}) unless Bugzilla->user->in_group('Testers'); + +my $action = $cgi->param('action') || ''; + +if ($action eq 'Add'){ + my $name = $cgi->param('name'); + my $product = $cgi->param('product') || $cgi->param('product_id'); + + my $env = Bugzilla::Testopia::Environment->create({ + name => $name, + product_id => $product, + }); + + $vars->{'tr_message'} = "The environment '$name' was successfully added."; + + my $category = Bugzilla::Testopia::Environment::Category->new({}); + if (Bugzilla->params->{'useclassification'}){ + $vars->{'allhaschild'} = $category->get_all_child_count; + $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; + $vars->{'type'} = 'classification'; + } + else { + $vars->{'toplevel'} = $category->get_env_product_list; + $vars->{'type'} = 'product'; + } + $vars->{'user'} = Bugzilla->user; + $vars->{'action'} = 'do_edit'; + $vars->{'environment'} = $env; + $template->process("testopia/environment/show.html.tmpl", $vars) + || print $template->error(); + +} + +else { + $vars->{'environment'} = Bugzilla::Testopia::Environment->new({}); + $vars->{'backlink'} = $vars->{'environment'}; + $vars->{'products'} = Bugzilla->user->get_selectable_products; + + $template->process("testopia/environment/add.html.tmpl", $vars) + || print $template->error(); +} diff --git a/tr_new_plan.cgi b/tr_new_plan.cgi new file mode 100755 index 0000000..3cd300c --- /dev/null +++ b/tr_new_plan.cgi @@ -0,0 +1,128 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Util; +use JSON; + +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Constants; + +############################################################################### +# tr_new_plan.cgi +# Presents a webform to the user for the creation of a new test plan. +# Creates a new testplan via Ajax +# +# INTERFACE: +# action: +# undef - Present form for new plan creation +# "add" - Form has been submitted with plan data. Create the test +# plan. +# +# product_id: integer - (REQUIRED) ID of the Product the new plan should be created in +# type: integer - (REQUIRED) ID of the Plan type +# prod_version: string - (REQUIRED) Version of the product to associate +# plan_name: string - (REQUIRED) Limited to 255 chars. Name for the plan +# plandoc: string - (OPTIONAL) HTML document describing the test plan +# file1-5: fileloc - (OPTIONAL) Path to a file to upload +# file_desc1-5: string - (OPTIONAL) Description of file to attach +# +# +################################################################################ + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; +print $cgi->header; + +my $action = $cgi->param('action') || ''; + +if ($action eq 'add'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + ThrowUserError("testopia-create-denied", {'object' => 'Test Plan'}) unless Bugzilla->user->in_group('Testers'); + + my $plan = Bugzilla::Testopia::TestPlan->create({ + 'product_id' => $cgi->param('product_id'), + 'author_id' => Bugzilla->user->id, + 'type_id' => $cgi->param('type'), + 'default_product_version' => $cgi->param('prod_version'), + 'name' => $cgi->param('plan_name'), + 'text' => $cgi->param("plandoc") || '', + }); + + my $err = JSON::false; + for (my $i=1; $i<5; $i++){ + next unless defined $cgi->upload("file$i"); + + my $fh = $cgi->upload("file$i"); + my $data; + # enable 'slurp' mode + local $/; + $data = <$fh>; + $data || ThrowUserError("zero_length_file"); + + Bugzilla->error_mode(ERROR_MODE_DIE); + eval { + my $attachment = Bugzilla::Testopia::Attachment->create({ + plan_id => $plan->id, + submitter_id => Bugzilla->user->id, + description => $cgi->param("file_desc$i") || 'Attachment', + filename => $cgi->upload("file$i"), + mime_type => $cgi->uploadInfo($cgi->param("file$i"))->{'Content-Type'}, + contents => $data + }); + }; + if ($@){ + $err = JSON::true; + } + } + + print "{success: true, plan: '". $plan->id ."', err: $err}"; + +} + +#################### +### Display Form ### +#################### +else { + my $product; + if ($cgi->param('product_id')){ + $product = Bugzilla::Testopia::Product->new($cgi->param('product_id')); + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + $vars->{'product'} = $product; + } + + ThrowUserError("testopia-create-denied", {'object' => 'Test Plan'}) unless Bugzilla->user->in_group('Testers'); + $vars->{'plan'} = Bugzilla::Testopia::TestPlan->new({}); + $template->process("testopia/plan/add.html.tmpl", $vars) || + ThrowTemplateError($template->error()); +} diff --git a/tr_new_run.cgi b/tr_new_run.cgi new file mode 100755 index 0000000..adb072f --- /dev/null +++ b/tr_new_run.cgi @@ -0,0 +1,143 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::User; + +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Constants; + +############################################################################### +# tr_new_run.cgi +# Presents a webform to the user for the creation of a new test run. +# +# INTERFACE: +# plan_id: The id of the plan this run will belong to. +# If no plan_id is found, the user will first +# be presented with a form to select a plan. +# +# action: undef - Present form for new run creation +# "Add" - Form has been submitted with run data. Create the test +# run. +# +################################################################################ + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); +my $cgi = Bugzilla->cgi; +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $plan_id = $cgi->param('plan_id'); + +unless ($plan_id){ + $vars->{'form_action'} = 'tr_new_run.cgi'; + $vars->{'type'} = "Run"; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +validate_test_id($plan_id, 'plan'); +my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); +# Users need write permission on the plan in order to create a test run for +# that plan. See tr_plan_access.cgi +ThrowUserError("testopia-create-denied", {'object' => 'Test Run', 'plan' => $plan}) unless ($plan->canedit); + +if ($action eq 'add'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + my $prod_version = $cgi->param('prod_version') ? $cgi->param('prod_version') : $plan->product_version(); + my $build = trim($cgi->param('build')); + my $env = trim($cgi->param('environment')); + + if ($cgi->param('new_build')){ + my $b = Bugzilla::Testopia::Build->create({ + 'name' => $cgi->param('new_build'), + 'milestone' => '---', + 'product_id' => $plan->product_id, + 'description' => '', + 'isactive' => 1, + }); + $build = $b->id; + } + + if ($cgi->param('new_env')){ + my $e = Bugzilla::Testopia::Environment->create({ + 'name' => $cgi->param('new_env'), + 'product_id' => $plan->product_id, + 'isactive' => 1, + }); + $env = $e->id; + } + + my $run = Bugzilla::Testopia::TestRun->create({ + 'plan_id' => $plan->id, + 'environment_id' => $env, + 'build_id' => $build, + 'product_version' => $prod_version, + 'plan_text_version' => $plan->version, + 'manager_id' => $cgi->param('manager'), + 'summary' => $cgi->param('summary'), + 'notes' => $cgi->param('notes') || '', + 'target_pass' => $cgi->param('target_pass'), + 'target_completion' => $cgi->param('target_completion'), + 'status' => 1, + }); + + if ($cgi->param('getall')){ + $cgi->delete_all; + $cgi->param('plan_id', $plan->id); + $cgi->param('current_tab', 'case'); + $cgi->param('case_status', 'CONFIRMED'); + $cgi->param('viewall', 1); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $ref = Bugzilla->dbh->selectcol_arrayref($search->query); + foreach my $case_id (@$ref){ + $run->add_case_run($case_id); + } + } + else { + foreach my $case_id (split(',', $cgi->param('case_ids'))){ + $run->add_case_run($case_id); + } + } + print "{success: true, run_id: " . $run->id ."}"; +} + +#################### +### Display Form ### +#################### +else { + $vars->{'plan'} = $plan; + $vars->{'case'} = Bugzilla::Testopia::TestCase->new({}); + $template->process("testopia/run/add.html.tmpl", $vars) || + ThrowTemplateError($template->error()); +} diff --git a/tr_plan_access.cgi b/tr_plan_access.cgi new file mode 100644 index 0000000..5d80e34 --- /dev/null +++ b/tr_plan_access.cgi @@ -0,0 +1,137 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Testopia::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::TestPlan; + +use vars qw($vars); + +local our $template = Bugzilla->template; +local our $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); +Bugzilla->error_mode(ERROR_MODE_AJAX); + +print $cgi->header; + +my $plan_id = trim($cgi->param('plan_id') || ''); +my $action = $cgi->param('action') || ''; + +unless (detaint_natural($plan_id)){ + $vars->{'form_action'} = 'tr_show_plan.cgi'; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +validate_test_id($plan_id, 'plan'); +local our $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + +if ($action eq 'edit'){ + ThrowUserError('testopia-plan-acl-denied', {plan_id => $plan->id}) unless ($plan->canadmin); + do_update(); + print "{success: true}"; +} +elsif ($action eq 'add_user'){ + ThrowUserError('testopia-plan-acl-denied', {plan_id => $plan->id}) unless ($plan->canadmin); + do_update(); + my $userid = login_to_id(trim($cgi->param('adduser'))); + ThrowUserError("invalid_username", { name => $cgi->param('adduser')}) unless $userid; + ThrowUserError('testopia-tester-already-on-list', {'login' => $cgi->param('adduser')}) + if ($plan->check_tester($userid)); + + my $perms = 0; + + $perms |= TR_READ if $cgi->param("nr"); + $perms |= TR_READ | TR_WRITE if $cgi->param("nw"); + $perms |= TR_READ | TR_WRITE | TR_DELETE if $cgi->param("nd"); + $perms |= TR_READ | TR_WRITE | TR_DELETE | TR_ADMIN if $cgi->param("na"); + + detaint_natural($perms); + trick_taint($userid); + $plan->add_tester($userid, $perms); + + print "{success: true}"; +} +elsif ($action eq 'delete'){ + ThrowUserError('testopia-plan-acl-denied', {plan_id => $plan->id}) unless ($plan->canadmin); + my $userid = $cgi->param('user'); + detaint_natural($userid); + my $user = Bugzilla::User->new($userid); + ThrowUserError('baduser') unless $user; + ThrowUserError('testopia-no-admins') unless $plan->has_admin($user->id) > 0; + $plan->remove_tester($user->id); + print "{success: true, action: 'Removed User', value: '" . $user->login ."'}"; + exit; +} + +else{ + $vars->{'plan'} = $plan; + $vars->{'user'} = Bugzilla->user; + $template->process("testopia/plan/access-list.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + +} + +sub do_update { + # We need at least one admin + my $params = join(" ", $cgi->param()); + if (scalar @{$plan->access_list} > 0){ + ThrowUserError('testopia-no-admins') unless $params =~ /(^|\s)a\d+($|\s)/; + } + + my $tester_regexp = $cgi->param('userregexp'); + trick_taint($tester_regexp); + + my $regexp_perms = 0; + + # Each permission implies the prior ones. + $regexp_perms |= TR_READ if $cgi->param('pr'); + $regexp_perms |= TR_READ | TR_WRITE if $cgi->param('pw'); + $regexp_perms |= TR_READ | TR_WRITE | TR_DELETE if $cgi->param('pd'); + $regexp_perms |= TR_READ | TR_WRITE | TR_DELETE | TR_ADMIN if $cgi->param('pa'); + + detaint_natural($regexp_perms); + $plan->set_tester_regexp($tester_regexp, $regexp_perms); + + foreach my $row (@{$plan->access_list}){ + my $perms = 0; + + $perms |= TR_READ if $cgi->param('r'.$row->{'user'}->id); + $perms |= TR_READ | TR_WRITE if $cgi->param('w'.$row->{'user'}->id); + $perms |= TR_READ | TR_WRITE | TR_DELETE if $cgi->param('d'.$row->{'user'}->id); + $perms |= TR_READ | TR_WRITE | TR_DELETE | TR_ADMIN if $cgi->param('a'.$row->{'user'}->id); + + detaint_natural($perms); + $plan->update_tester($row->{'user'}->id, $perms); + } + $vars->{'tr_message'} = " Access updated"; +} diff --git a/tr_plan_reports.cgi b/tr_plan_reports.cgi new file mode 100755 index 0000000..3781244 --- /dev/null +++ b/tr_plan_reports.cgi @@ -0,0 +1,212 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Report; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); + +my $type = $cgi->param('type') || ''; + +if ($type eq 'build_coverage'){ + my $plan_id = trim(Bugzilla->cgi->param('plan_id') || ''); + + unless ($plan_id){ + $vars->{'form_action'} = 'tr_plan_reports.cgi'; + $vars->{'type'} = 'build_coverage'; + print $cgi->header; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } + validate_test_id($plan_id, 'plan'); + my $action = $cgi->param('action') || ''; + my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + ThrowUserError("testopia-permission-denied", {'object' => $plan}) unless $plan->canview; + my $report = {}; + my %buildseen; + foreach my $case (@{$plan->test_cases}){ + foreach my $cr (@{$case->caseruns}){ + $buildseen{$cr->build->id} = $cr->build->name; + $report->{$case->id}->{$cr->build->id} = $cr; + } + $report->{$case->id}->{'name'} = $case->summary; + } + my $run_reports = {}; + foreach my $run (@{$plan->test_runs}){ + foreach my $cr (@{$run->caseruns}){ + $run_reports->{$run->id}->{$cr->case->id}->{$cr->build->id} = $cr; + $run_reports->{$run->id}->{$cr->case->id}->{'name'} = $cr->case->summary; + } + + $run_reports->{$run->id}->{'name'} = $run->summary; + } + my @ids = keys %buildseen; + $report->{'build_ids'} = \@ids; + $report->{'builds'} = \%buildseen; + $vars->{'run_reports'} = $run_reports; + $vars->{'report'} = $report; + $vars->{'plan'} = $plan; + + print $cgi->header(); + if ($cgi->param('debug')){ + use Data::Dumper; + print Dumper($report); + print Dumper($run_reports); + } + $template->process("testopia/reports/build-coverage.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + +} +elsif ($type eq 'bugcounts'){ + my $plan_id = trim(Bugzilla->cgi->param('plan_id') || ''); + + unless ($plan_id){ + $vars->{'form_action'} = 'tr_plan_reports.cgi'; + $vars->{'type'} = 'bugcounts'; + print $cgi->header; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } + validate_test_id($plan_id, 'plan'); + my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + ThrowUserError("testopia-permission-denied", {'object' => $plan}) unless $plan->canview; + + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectall_arrayref( + "SELECT COUNT(bug_id) AS casecount, bug_id FROM test_case_bugs + INNER JOIN test_cases ON test_cases.case_id = test_case_bugs.case_id + INNER JOIN test_case_plans ON test_case_plans.case_id = test_cases.case_id + INNER JOIN test_plans ON test_case_plans.plan_id = test_plans.plan_id + WHERE test_plans.plan_id = ? " . + $dbh->sql_group_by("test_cases.case_id", "test_case_bugs.bug_id"), + {'Slice'=>{}}, $plan->id); + + $vars->{'bug_table'} = $ref; + $vars->{'plan'} = $plan; + + print $cgi->header; + $template->process("testopia/reports/bug-count.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +elsif ($type eq 'untested'){ + my $plan_id = trim(Bugzilla->cgi->param('plan_id') || ''); + unless ($plan_id){ + $vars->{'form_action'} = 'tr_plan_reports.cgi'; + $vars->{'type'} = 'bugcounts'; + print $cgi->header; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } + validate_test_id($plan_id, 'plan'); + my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); + ThrowUserError("testopia-permission-denied", {'object' => $plan}) unless $plan->canview; + + my $dbh = Bugzilla->dbh; + my $ref = $dbh->selectcol_arrayref( + "SELECT test_cases.case_id + FROM test_cases + INNER JOIN test_case_plans ON test_cases.case_id = test_case_plans.case_id + WHERE test_cases.case_id NOT IN ( + SELECT DISTINCT test_case_runs.case_id + FROM test_case_runs + INNER JOIN test_runs ON test_case_runs.run_id = test_runs.run_id + WHERE test_runs.plan_id = ?) + AND test_case_plans.plan_id = ?", undef, ($plan_id, $plan_id)); + + $vars->{'case_ids'} = join(",", @$ref); + $vars->{'case_count'} = scalar @$ref; + $vars->{'plan_id'} = $plan_id; + + print $cgi->header; + $template->process("testopia/reports/untested.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else{ + $cgi->param('current_tab', 'plan'); + $cgi->param('viewall', 1); + my $report = Bugzilla::Testopia::Report->new('plan', 'tr_list_plans.cgi', $cgi); + $vars->{'report'} = $report; + $vars->{'qname'} = $cgi->param('qname'); + + ### From Bugzilla report.cgi by Gervase Markham + my $formatparam = $cgi->param('format'); + my $report_action = $cgi->param('report_action'); + if ($report_action eq "data") { + # So which template are we using? If action is "wrap", we will be using + # no format (it gets passed through to be the format of the actual data), + # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else). + # report.html.tmpl produces an HTML framework for either tables of HTML + # data, or images generated by calling report.cgi again with action as + # "plot". + $formatparam =~ s/[^a-zA-Z\-]//g; + trick_taint($formatparam); + $vars->{'format'} = $formatparam; + $formatparam = ''; + } + elsif ($report_action eq "plot") { + # If action is "plot", we will be using a format as normal (pie, bar etc.) + # and a ctype as normal (currently only png.) + $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0; + $vars->{'x_labels_vertical'} = $cgi->param('x_labels_vertical') ? 1 : 0; + $vars->{'data'} = $report->{'image_data'}; + } + else { + ThrowCodeError("unknown_action", {action => $cgi->param('report_action')}); + } + + my $format = $template->get_format("testopia/reports/report", $formatparam, + scalar($cgi->param('ctype'))); + + my @time = localtime(time()); + my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; + my $filename = "report-" . $date . ".$format->{extension}"; + + my $disp = "inline"; + # We set CSV files to be downloaded, as they are designed for importing + # into other programs. + if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + } + + print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + + $vars->{'time'} = $date; + $template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); + + exit; +} diff --git a/tr_process_case.cgi b/tr_process_case.cgi new file mode 100755 index 0000000..1ddd755 --- /dev/null +++ b/tr_process_case.cgi @@ -0,0 +1,230 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Bug; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Attachment; +use Bugzilla::Testopia::Constants; +use JSON; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $action = $cgi->param('action') || ''; + +my $case = Bugzilla::Testopia::TestCase->new($cgi->param('case_id')); + +unless ($case){ + print $cgi->header; + ThrowUserError('testopia-missing-object',{object => 'case'}); +} + +if ($action eq 'edit'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + + $case->set_alias($cgi->param('alias')) if exists $cgi->{param}->{'alias'} || exists $cgi->{'alias'}; + $case->set_category($cgi->param('category')) if $cgi->param('category'); + $case->set_case_status($cgi->param('status')) if $cgi->param('status'); + $case->set_priority($cgi->param('priority')) if $cgi->param('priority'); + $case->set_isautomated($cgi->param('isautomated') eq 'on' ? 1 : 0) if $cgi->param('isautomated'); + $case->set_script($cgi->param('script')) if exists $cgi->{param}->{'script'} || exists $cgi->{'script'}; + $case->set_arguments($cgi->param('arguments')) if exists $cgi->{param}->{'arguments'} || exists $cgi->{'arguments'}; + $case->set_summary($cgi->param('summary')) if $cgi->param('summary'); + $case->set_requirement($cgi->param('requirement')) if exists $cgi->{param}->{'requirement'} || exists $cgi->{'requirement'}; + $case->set_dependson($cgi->param('tcdependson')) if exists $cgi->{param}->{'tcdependson'} || exists $cgi->{'tcdependson'}; + $case->set_blocks($cgi->param('tcblocks')) if exists $cgi->{param}->{'tcblocks'} || exists $cgi->{'tcblocks'}; + $case->set_default_tester($cgi->param('tester')) if exists $cgi->{param}->{'tester'} || exists $cgi->{'tester'}; + $case->set_estimated_time($cgi->param('estimated_time')) if exists $cgi->{param}->{'estimated_time'} || exists $cgi->{'estimated_time'}; + $case->set_sortkey($cgi->param('sortkey')) if exists $cgi->{param}->{'sortkey'}; + + $case->add_to_run($cgi->param('addruns')); + $case->add_tag($cgi->param('newtag')); + $case->attach_bug($cgi->param('bugs'),$cgi->param('caserun_id')); + + $case->update(); + + print "{'success': true, 'tcase': " . $case->TO_JSON ."}"; +} + +elsif ($action eq 'update_doc'){ + + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + + my $newtcaction = $cgi->param('tcaction') || '' if $cgi->param('tcaction'); + my $newtceffect = $cgi->param('tceffect') || '' if $cgi->param('tceffect'); + my $newtcsetup = $cgi->param('tcsetup') || '' if $cgi->param('tcsetup'); + my $newtcbreakdown = $cgi->param('tcbreakdown') || '' if $cgi->param('tcbreakdown'); + + if($case->diff_case_doc($newtcaction, $newtceffect, $newtcsetup, $newtcbreakdown) ne ''){ + $case->store_text($case->id, Bugzilla->user->id, $newtcaction, $newtceffect, $newtcsetup, $newtcbreakdown); + } +} + +elsif ($action eq 'link') { + print $cgi->header; + my @plans; + foreach my $id (split(',', $cgi->param('plan_ids'))){ + my $plan = Bugzilla::Testopia::TestPlan->new($id); + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + push @plans, $plan; + } + ThrowUserError('missing-plans-list') unless scalar @plans; + + foreach my $plan (@plans){ + $case->link_plan($plan->id); + } + + delete $case->{'plans'}; + + print "{'success': true}"; +} + +elsif ($action eq 'unlink'){ + print $cgi->header; + my $plan_id = $cgi->param('plan_id'); + validate_test_id($plan_id, 'plan'); + ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->can_unlink_plan($plan_id)); + ThrowUserError('testopia-case-unlink-failure') unless $case->unlink_plan($plan_id); + + print "{'success': true}"; +} + +elsif ($action eq 'detachbug'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + my @buglist; + foreach my $bug (split(/[\s,]+/, $cgi->param('bug_id'))){ + ValidateBugID($bug); + push @buglist, $bug; + } + foreach my $bug (@buglist){ + $case->detach_bug($bug); + } + print "{'success': true}"; +} + +elsif ($action eq 'delete'){ + print $cgi->header; + ThrowUserError("testopia-no-delete", {'object' => $case}) unless $case->candelete; + + $case->obliterate; + print "{'success': true}"; +} + +elsif ($action eq 'addcomponent' || $action eq 'removecomponent'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + my $comp = $cgi->param('component_id'); + + if ($action eq 'addcomponent'){ + foreach my $c (@{$case->components}){ + if ($c->id == $comp){ + exit; + } + } + $case->add_component($comp); + } + else { + $case->remove_component($comp); + } + print "{'success': true}"; +} + +elsif ($action eq 'getbugs'){ + print $cgi->header; + ThrowUserError("testopia-permission-denied", {'object' => $case}) unless $case->canview; + my @bugs; + foreach my $bug (@{$case->bugs}){ + push @bugs, { bug_id => $bug->bug_id, + summary => $bug->short_desc, + case_run_id => $bug->{'case_run_id'}, + status => $bug->bug_status, + resolution => $bug->resolution, + assignee => $bug->assigned_to->name, + severity => $bug->bug_severity, + priority => $bug->priority, + build => $bug->{'build'}, + env => $bug->{'env'}, + run_id => $bug->{'run_id'} || '', + }; + } + my $json = new JSON; + print "{'bugs':" . $json->encode(\@bugs) . "}"; +} + +elsif ($action eq 'getplans'){ + print $cgi->header; + ThrowUserError("testopia-permission-denied", {'object' => $case}) unless $case->canview; + my @plans; + foreach my $p (@{$case->plans}){ + push @plans, { plan_id => $p->id, plan_name => $p->name }; + } + my $json = new JSON; + print "{'plans':" . $json->encode(\@plans) . "}"; +} + +elsif($action eq 'getcomponents'){ + print $cgi->header; + ThrowUserError("testopia-permission-denied", {'object' => $case}) unless $case->canview; + my @comps; + foreach my $c (@{$case->components}){ + push @comps, {'id' => $c->id, 'name' => $c->name}; + } + my $json = new JSON; + print "{'comps':" . $json->encode(\@comps) . "}"; + +} + +elsif ($action eq 'case_to_bug'){ + + ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit; + $case->text; + $case->{text}->{action} =~ s/(]+||)/\n\n/g; + $case->{text}->{action} =~ s/<.*?>//g; + my $vars; + $vars->{'caserun'} = Bugzilla::Testopia::TestCaseRun->new($cgi->param('caserun_id')) if $cgi->param('caserun_id'); + $vars->{'case'} = $case; + + print $cgi->header(-type => 'text/xml'); + Bugzilla->template->process("testopia/case/new-bug.xml.tmpl", $vars) || + ThrowTemplateError(Bugzilla->template->error()); +} + +else { + print $cgi->header; + ThrowUserError("testopia-no-action"); +} diff --git a/tr_process_plan.cgi b/tr_process_plan.cgi new file mode 100755 index 0000000..9c5b72e --- /dev/null +++ b/tr_process_plan.cgi @@ -0,0 +1,244 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::Category; +use Bugzilla::Testopia::Build; +use Bugzilla::Testopia::Attachment; +use JSON; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; +my $action = $cgi->param('action') || ''; + +my $plan = Bugzilla::Testopia::TestPlan->new($cgi->param('plan_id')); + +unless ($plan){ + print $cgi->header; + ThrowUserError('testopia-missing-object',{object => 'plan'}); +} + +### Archive or Unarchive ### +if ($action eq 'archive' || $action eq 'unarchive'){ + print $cgi->header; + + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + + $plan->toggle_archive(Bugzilla->user->id); + + print '{"success" : true}'; + exit; + +} + +elsif ($action eq 'clone'){ + print $cgi->header; + ThrowUserError("testopia-create-denied", {object => 'plan'}) unless (Bugzilla->user->in_group('Testers')); + + my $plan_name = $cgi->param('plan_name'); + my $product_id = $cgi->param('product_id'); + my $version = $cgi->param('prod_version'); + my $product = Bugzilla::Testopia::Product->new($product_id); + $product ||= $plan->product; + + trick_taint($plan_name); + trick_taint($version); + detaint_natural($product_id); + Bugzilla::Version->check({product => $product, name => $version}); + if ($cgi->param('copy_runs')){ + ThrowUserError("invalid-test-id-non-existent", + {'id' => $cgi->param('new_run_build'), 'type' => 'Build'}) unless $cgi->param('new_run_build'); + ThrowUserError("invalid-test-id-non-existent", + {'id' => $cgi->param('new_run_env'), 'type' => 'Environment'}) unless $cgi->param('new_run_env'); + } + my $author = $cgi->param('keep_plan_author') ? $plan->author->id : Bugzilla->user->id; + my $newplanid = $plan->clone($plan_name, $author, $product_id, $version, $cgi->param('copy_doc')); + my $newplan = Bugzilla::Testopia::TestPlan->new($newplanid); + + if ($cgi->param('copy_tags')){ + foreach my $tag (@{$plan->tags}){ + $newplan->add_tag($tag->name); + } + } + if ($cgi->param('copy_attachments')){ + foreach my $att (@{$plan->attachments}){ + $att->link_plan($newplanid); + } + } + if ($cgi->param('copy_perms')){ + $plan->copy_permissions($newplanid); + $newplan->add_tester($author, TR_READ | TR_WRITE | TR_DELETE | TR_ADMIN ) unless ($cgi->param('keep_plan_author')); + $newplan->derive_regexp_testers($plan->tester_regexp); + } + else { + # Give the author admin rights + $newplan->add_tester($author, TR_READ | TR_WRITE | TR_DELETE | TR_ADMIN ); + $newplan->set_tester_regexp( Bugzilla->params->{"testopia-default-plan-testers-regexp"}, 3) + if Bugzilla->params->{"testopia-default-plan-testers-regexp"}; + $newplan->derive_regexp_testers(Bugzilla->params->{'testopia-default-plan-testers-regexp'}) + } + if ($cgi->param('copy_cases')){ + my @cases = @{$plan->test_cases}; + my $total = scalar @cases; + foreach my $case (@cases){ + # Copy test cases creating new ones + if ($cgi->param('make_copy')){ + my $case_author = $cgi->param('keep_case_authors') ? $case->author->id : Bugzilla->user->id; + my $case_tester = $cgi->param('keep_tester') ? $case->default_tester->id : Bugzilla->user->id; + my $category; + if ($cgi->param('copy_categories')){ + my $category_id = check_case_category($case->category->name, $product); + if (! $category_id){ + $category = Bugzilla::Testopia::Category->create({ + product_id => $product->id, + name => $case->category->name, + description => $case->category->description, + }); + } + else { + $category = Bugzilla::Testopia::Category->new($category_id); + } + + } + else { + if ($product->id == $plan->product_id){ + $category = $case->category; + } + else{ + my @categories = @{$product->categories}; + if (scalar @categories < 1){ + $category = Bugzilla::Testopia::Category->create({ + product_id => $product->id, + name => '--default--', + description => 'Default product category for test cases', + }); + + } + else{ + $category = $categories[0]; + } + } + } + + my $caseid = $case->copy($case_author, $case_tester, 1, $category->id); + my $newcase = Bugzilla::Testopia::TestCase->new($caseid); + + $newcase->link_plan($newplan->id, $caseid); + + foreach my $tag (@{$case->tags}){ + $newcase->add_tag($tag->name); + } + + foreach my $comp (@{$case->components}){ + $newcase->add_component($comp->{'id'}); + } + } + # Just create a link + else { + $case->link_plan($newplan->id); + } + } + } + if ($cgi->param('copy_runs')){ + foreach my $run (@{$plan->test_runs}){ + my $manager = $cgi->param('keep_run_managers') ? $run->manager->id : Bugzilla->user->id; + + my $build = Bugzilla::Testopia::Build->new($cgi->param('new_run_build')); + my $env = Bugzilla::Testopia::Build->new($cgi->param('new_run_env')); + + my $run_id = $run->clone($run->summary, $manager, $newplan->id, $build->id, $env->id); + + my $newrun = Bugzilla::Testopia::TestRun->new($run_id); + if($cgi->param('copy_run_tags')){ + foreach my $tag (@{$run->tags}){ + $newrun->add_tag($tag->name); + } + } + if($cgi->param('copy_run_cases')){ + foreach my $cr (@{$run->current_caseruns}){ + $newrun->add_case_run($cr->case_id, $cr->sortkey); + } + } + } + } + print '{"success" : true, "plan_id" : ' . $newplan->id . "}"; + exit; +} + +elsif ($action eq 'delete'){ + print $cgi->header; + ThrowUserError("testopia-no-delete", {'object' => $plan}) unless ($plan->candelete); + + $plan->obliterate; + + print '{"success" : true}'; + exit; +} + +elsif ($action eq 'edit'){ + print $cgi->header; + ThrowUserError("testopia-read-only", {'object' => $plan}) unless $plan->canedit; + + $plan->set_default_product_version($cgi->param('prod_version')) if $cgi->param('prod_version'); + $plan->set_type($cgi->param('type')) if $cgi->param('type'); + $plan->set_name($cgi->param('name')) if $cgi->param('name'); + + if(exists $cgi->{"plandoc"} || exists $cgi->{'param'}->{"plandoc"}){ + my $newdoc = $cgi->param("plandoc"); + if($plan->diff_plan_doc($newdoc) ne ''){ + $plan->store_text($plan->id, Bugzilla->user->id, $newdoc); + } + } + + $plan->update(); + + print '{"success" : true}'; + exit; +} + +elsif ($action eq 'getfilter'){ + my $vars; + $vars->{'case'} = Bugzilla::Testopia::TestCase->new({}); + $vars->{'plan'} = $plan; + + print $cgi->header; + + Bugzilla->template->process("testopia/case/filter.html.tmpl", $vars) || + ThrowTemplateError(Bugzilla->template->error()); +} + +else { + print $cgi->header; + ThrowUserError("testopia-no-action"); +} \ No newline at end of file diff --git a/tr_process_run.cgi b/tr_process_run.cgi new file mode 100755 index 0000000..9bcebb8 --- /dev/null +++ b/tr_process_run.cgi @@ -0,0 +1,158 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Joel Smith + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +use JSON; + +Bugzilla->error_mode(ERROR_MODE_AJAX); +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $run = Bugzilla::Testopia::TestRun->new($cgi->param('run_id')); +unless ($run){ + print $cgi->header; + ThrowUserError('testopia-missing-object',{object => 'run'}); +} + +if ($action eq 'edit'){ + ThrowUserError("testopia-read-only", {'object' => $run}) unless $run->canedit; + ThrowUserError("testopia-no-status", {field => 'status'}) if $cgi->param('status') && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'manager'}) if $cgi->param('manager') && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'target'}) if (exists $cgi->{'target_pass'} || exists $cgi->{param}->{'target_pass'}) && !$run->canstatus; + ThrowUserError("testopia-no-status", {field => 'target'}) if (exists $cgi->{'target_completion'} || exists $cgi->{param}->{'target_completion'}) && !$run->canstatus; + + my $timestamp; + $timestamp = $run->stop_date; + $timestamp = undef if $cgi->param('status'); + $timestamp = get_time_stamp() if ($cgi->param('status') == 0 || $cgi->param('status') eq 'STOPPED') && !$run->stop_date; + + $run->set_summary($cgi->param('summary')) if $cgi->param('summary'); + $run->set_product_version($cgi->param('run_product_version')) if $cgi->param('run_product_version'); + $run->set_plan_text_version($cgi->param('plan_version')) if $cgi->param('plan_version'); + $run->set_build($cgi->param('build')) if $cgi->param('build'); + $run->set_environment($cgi->param('environment')) if $cgi->param('environment'); + $run->set_manager($cgi->param('manager')) if $cgi->param('manager'); + $run->set_notes($cgi->param('run_notes')) if exists $cgi->{'run_notes'} || exists $cgi->{param}->{'run_notes'}; + $run->set_stop_date($timestamp) if $cgi->param('status'); + $run->set_target_pass($cgi->param('target_pass')) if exists $cgi->{'target_pass'} || exists $cgi->{param}->{'target_pass'}; + $run->set_target_completion($cgi->param('target_completion')) if exists $cgi->{'target_completion'} || exists $cgi->{param}->{'target_completion'}; + + $run->update(); + + print "{success: true}"; + +} + +elsif ($action eq 'delete'){ + ThrowUserError("testopia-no-delete", {'object' => $run}) unless ($run->candelete); + $run->obliterate; + print "{'success': true}"; +} + +elsif ($action eq 'save_filter'){ + my $dbh = Bugzilla->dbh; + ThrowUserError('query_name_missing') unless $cgi->param('query_name'); + ThrowUserError("testopia-read-only", {'object' => $run}) unless $run->canedit; + + my $qname = '__run_id_' . $run->id . '_' . $cgi->param('query_name'); + my $query = $cgi->canonicalise_query('action'); + + trick_taint($query); + trick_taint($qname); + + my ($name) = $dbh->selectrow_array( + "SELECT name + FROM test_named_queries + WHERE name = ?", + undef,($qname)); + + if ($name){ + $dbh->do( + "UPDATE test_named_queries + SET query = ? + WHERE name = ?", + undef,($query, $qname)); + } + else{ + my $quoted_qname = url_quote($qname); + $dbh->do("INSERT INTO test_named_queries + VALUES(?,?,?,?,?)", + undef, (Bugzilla->user->id, $qname, 0, $query, SAVED_FILTER)); + + } + print "{'success':true}"; +} + +elsif ($action eq 'delete_filter'){ + my $dbh = Bugzilla->dbh; + ThrowUserError('query_name_missing') unless $cgi->param('query_name'); + ThrowUserError("testopia-read-only", {'object' => $run}) unless $run->canedit; + my $qname = $cgi->param('query_name'); + trick_taint($qname); + $qname = '__run_id_' . $run->id . '_' . $qname; + + $dbh->do( + "DELETE FROM test_named_queries + WHERE name = ?", + undef,($qname)); + + print "{'success':true}"; +} + +elsif ($action eq 'getfilters'){ + my $dbh = Bugzilla->dbh; + ThrowUserError("testopia-read-only", {'object' => $run}) unless $run->canedit; + my $qnameregexp = '__run_id_' . $run->id . '_'; + + my $filters = $dbh->selectall_arrayref( + "SELECT name, query + FROM test_named_queries + WHERE name REGEXP(?)", + {'Slice' => {}},($qnameregexp)); + foreach my $f (@$filters){ + $f->{'name'} =~ s/^__run_id_\d+_//; + } + print "{'filters':" . to_json($filters) . "}"; +} + +else { + print $cgi->header; + ThrowUserError("testopia-no-action"); +} + \ No newline at end of file diff --git a/tr_product_reports.cgi b/tr_product_reports.cgi new file mode 100755 index 0000000..dd2fd5f --- /dev/null +++ b/tr_product_reports.cgi @@ -0,0 +1,125 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +print $cgi->header; +use Data::Dumper; + +Bugzilla->login(LOGIN_REQUIRED); + +############################################################################### +# tr_show_product.cgi +# Displays product level information including builds, categories, environments +# and tags as well as provides product level reports. +# +# INTERFACE: +# product_id: product to display +# action: +# +################################################################################ + +my $product; + +if ($cgi->param('product_id')){ + $product = Bugzilla::Testopia::Product->new($cgi->param('product_id')); + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit; + $vars->{'product'} = $product; +} + +my $type = $cgi->param('type') || ''; +my $action = $cgi->param('action') || ''; +if ($action eq 'draw'){ + exit unless $product; + + my @data; + my $caserun = Bugzilla::Testopia::TestCaseRun->new({}); + my $run = Bugzilla::Testopia::TestRun->new({}); + + my @names; + my @values; + + if ($type eq 'completion'){ + my $open = 0; + my $closed = 0; + foreach my $status (@{$caserun->get_status_list}){ + if ($caserun->is_open_status($status->{'id'})){ + $open += $run->case_run_count($status->{'id'}, undef, undef, [$product]); + } + else { + $closed += $run->case_run_count($status->{'id'}, undef, undef, [$product]); + } + } + push @names, 'Completed', 'Not Completed'; + push @values, $closed, $open; + $vars->{'chart_title'} = 'Completion Percentage'; + $vars->{'colors'} = (['#56e871', '#FFFFFF']); + + } + elsif ($type eq 'passrate'){ + foreach my $status (@{$caserun->get_status_list}){ + if ($caserun->is_closed_status($status->{'id'})){ + push @names, $status->{'name'}; + push @values, $run->case_run_count($status->{'id'}, undef, undef, [$product]); + } + } + + $vars->{'chart_title'} = 'Pass/Fail Rate'; + $vars->{'colors'} = (['#56e871', '#ed3f58','#e17a56']); + + } + + elsif ($type eq 'breakdown'){ + foreach my $status (@{$caserun->get_status_list}){ + push @names, $status->{'name'}; + push @values, $run->case_run_count($status->{'id'}, undef, undef, [$product]); + } + $vars->{'chart_title'} = 'Status Breakdown'; + $vars->{'colors'} = (['#858aef', '#56e871', '#ed3f58', '#b8eae1', '#f1d9ab', '#e17a56']); + + } + + push @data, \@names; + push @data, \@values; + + $vars->{'width'} = 200; + $vars->{'height'} = 150; + $vars->{'data'} = \@data; + + + print $cgi->header; + $template->process("testopia/reports/report-pie.png.tmpl", $vars) + || ThrowTemplateError($template->error()); +} \ No newline at end of file diff --git a/tr_query.cgi b/tr_query.cgi new file mode 100755 index 0000000..084e14a --- /dev/null +++ b/tr_query.cgi @@ -0,0 +1,419 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Maciej Maczynski +# Ed Fuentetaja +# Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Field; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Classification; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Property; +use JSON; + +local our $vars = {}; + +my $dbh = Bugzilla->dbh; +local our $cgi = Bugzilla->cgi; +local our $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); +Bugzilla->error_mode(ERROR_MODE_AJAX); + +sub convert_bugzilla_fields { + my $field = shift; + my $values = get_legal_field_values($field); + my @vals; + foreach my $v (@$values){ + push @vals, {name => $v, id => $v}; + } + return \@vals; +} + +sub get_searchable_objects{ + my $object = shift; + my $dbh = Bugzilla->dbh; + + my $products = Bugzilla->user->get_selectable_products; + my @ids; + foreach my $p (@{$products}){ + push @ids, $p->id; + } + my $ref; + SWITCH: for ($object) { + /^components/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT name AS id, name + FROM components WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + + /^categories/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT name AS id, name + FROM test_case_categories WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + + /^builds/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT name AS id, name + FROM test_builds WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + + /^versions/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT value AS id, value AS name + FROM versions WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + + /^milestones/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT value AS id, value AS name + FROM milestones WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + + /^environments/ && do { + $ref = $dbh->selectall_arrayref( + "SELECT DISTINCT name AS id, name + FROM test_environments WHERE product_id IN (". join(",",@ids) .") + ORDER BY name", {'Slice'=>{}}); + last SWITCH; + }; + } + return $ref; +} + +my $action = $cgi->param('action') || ''; + +if ($action eq 'getversions'){ + print $cgi->header; + my @prod_ids; + + my $type = $cgi->param('type'); + my @values = split(",",$cgi->param('value')); + my $obj; + my @products; + my $prodinfo; + my $selectTypes = [qw{version milestone component build category environment}]; + + foreach my $value (@values){ + if ($type eq 'classification'){ + $obj = Bugzilla::Testopia::Classification->new({name => $value}); + next unless ($obj && scalar(grep {$_->name eq $obj->name} @{Bugzilla->user->get_selectable_classifications})); + push @products, @{$obj->user_visible_products()}; + my @prod_names; + push @prod_names, $_->name foreach @products; + $prodinfo->{'product'} = \@prod_names; + unshift @$selectTypes, "product"; + } + else { + trim($value); + $obj = Bugzilla::Product->new({name => $value}); + next unless ($obj && Bugzilla->user->can_see_product($obj->name)); + push @products, $obj; + } + } + + unless (scalar @products > 0){ + push @products, @{Bugzilla->user->get_selectable_products}; + unshift @$selectTypes, "product"; + my @prod_names; + push @prod_names, $_->name foreach @products; + $prodinfo->{'product'} = \@prod_names; + } + push @prod_ids, $_->id foreach (@products); + my $prod_ids = join(",", @prod_ids); + + my $dbh = Bugzilla->dbh; + $prodinfo->{'version'} = $dbh->selectcol_arrayref("SELECT DISTINCT value FROM versions WHERE product_id IN ($prod_ids)"); + $prodinfo->{'milestone'} = $dbh->selectcol_arrayref("SELECT DISTINCT value FROM milestones WHERE product_id IN ($prod_ids)"); + $prodinfo->{'component'} = $dbh->selectcol_arrayref("SELECT DISTINCT name FROM components WHERE product_id IN ($prod_ids)"); + $prodinfo->{'build'} = $dbh->selectcol_arrayref("SELECT DISTINCT name FROM test_builds WHERE product_id IN ($prod_ids)"); + $prodinfo->{'category'} = $dbh->selectcol_arrayref("SELECT DISTINCT name FROM test_case_categories WHERE product_id IN ($prod_ids)"); + $prodinfo->{'environment'} = $dbh->selectcol_arrayref("SELECT DISTINCT name FROM test_environments WHERE product_id IN ($prod_ids)"); + + # This list must match the name of the select fields and the names above + $prodinfo->{'selectTypes'} = $selectTypes; + + my $json = new JSON; + print "{'success': true, objects: " . $json->encode($prodinfo) . "}"; +} + +elsif ($action eq 'get_products'){ + print $cgi->header; + my @prod; + if (Bugzilla->params->{'useclassification'}){ + my @classes = $cgi->param('class_ids'); + foreach my $id (@classes){ + my $class = Bugzilla::Testopia::Classification->new($id); + push @prod, @{$class->user_visible_products}; + } + } + else { + @prod = Bugzilla->user->get_selectable_products; + } + + my $ret; + foreach my $e (@prod){ + $ret .= $e->{'id'}.'||'.$e->{'name'}.'|||'; + } + chop($ret); + print $ret; +} + +elsif ($action eq 'get_categories'){ + print $cgi->header; + my @prod_ids = $cgi->param('prod_id'); + my $ret; + + foreach my $prod_id (@prod_ids){ + detaint_natural($prod_id); + my $cat = Bugzilla::Testopia::Environment::Category->new({}); + my $cats_ref = $cat->get_element_categories_by_product($prod_id); + + foreach my $e (@{$cats_ref}){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + } + chop($ret); + print $ret; +} +elsif ($action eq 'get_elements'){ + print $cgi->header; + my @cat_ids = $cgi->param('cat_id'); + my $ret; + my @elmnts; + + foreach my $cat_id (@cat_ids){ + detaint_natural($cat_id); + + my $cat = Bugzilla::Testopia::Environment::Category->new($cat_id); + my $elmnts_ref = $cat->get_elements_by_category($cat->{'name'}); + + foreach my $e (@{$elmnts_ref}){ + push @elmnts, Bugzilla::Testopia::Environment::Element->new($e->{'element_id'}); + } + } + + foreach my $e (@elmnts){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + chop($ret); + print $ret; +} + +elsif ($action eq 'get_properties'){ + print $cgi->header; + my @elmnt_ids = $cgi->param('elmnt_id'); + my $ret; + + foreach my $elmnt_id (@elmnt_ids){ + detaint_natural($elmnt_id); + + my $elmnt = Bugzilla::Testopia::Environment::Element->new($elmnt_id); + my $props = $elmnt->get_properties(); + + foreach my $e (@{$props}){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + } + chop($ret); + print $ret; +} +elsif ($action eq 'get_valid_exp'){ + print $cgi->header; + my @prop_ids = $cgi->param('prop_id'); + my $ret; + + foreach my $prop_id (@prop_ids){ + detaint_natural($prop_id); + + my $prop = Bugzilla::Testopia::Environment::Property->new($prop_id); + my $exp = $prop->validexp; + + my @exps = split /\|/, $exp; + foreach my $exp (@exps){ + $ret .=$exp.'|||'; + } + } + chop($ret); + print $ret; +} +elsif ($action eq 'save_query'){ + print $cgi->header; + my $query = $cgi->param('query_part'); + my $qname = $cgi->param('query_name'); + my $type = $cgi->param('type'); + + ThrowUserError('query_name_missing') unless $qname; + + trick_taint($query); + trick_taint($qname); + detaint_natural($type); + $type ||= SAVED_SEARCH; + + my ($name) = $dbh->selectrow_array( + "SELECT name + FROM test_named_queries + WHERE userid = ? + AND name = ?", + undef,(Bugzilla->user->id, $qname)); + + if ($name){ + $dbh->do( + "UPDATE test_named_queries + SET query = ? + WHERE userid = ? + AND name = ?", + undef,($query, Bugzilla->user->id, $qname)); + $vars->{'tr_message'} = "Updated saved search '$qname'"; + } + else{ + my $quoted_qname = url_quote($qname); + $query .= "&qname=$quoted_qname" unless $type == 3; + $dbh->do("INSERT INTO test_named_queries + VALUES(?,?,?,?,?)", + undef, (Bugzilla->user->id, $qname, 1, $query, $type)); + + $vars->{'tr_message'} = "Search saved as '$qname'"; + } + + print "{'success': true}"; +} +elsif ($action eq 'delete_query'){ + print $cgi->header; + my $qname = $cgi->param('query_name'); + + trick_taint($qname); + + $dbh->do("DELETE FROM test_named_queries WHERE userid = ? AND name = ?", + undef, (Bugzilla->user->id, $qname)); + $vars->{'tr_message'} = "Testopia Saved Search '$qname' Deleted"; + + print "{'success': true}"; +} + +elsif ($action eq 'get_saved_searches'){ + print $cgi->header; + my $type = $cgi->param('type'); + my $user = $cgi->param('userid') || Bugzilla->user->id; + detaint_natural($type); + detaint_natural($user); + $type ||= SAVED_SEARCH; + + my $ref = $dbh->selectall_arrayref( + "SELECT tnq.name, query, profiles.realname AS author, type + FROM test_named_queries AS tnq + INNER JOIN profiles ON tnq.userid = profiles.userid + WHERE tnq.userid = ? AND isvisible = 1 + AND type = ?", + {'Slice' =>{}} ,($user, $type)); + + print $cgi->header; + print "{'searches':" . to_json($ref) . "}"; +} + +else{ + + #TODO: Support default query + my $tab = $cgi->param('current_tab') || ''; + if ($tab eq 'plan'){ + $vars->{'plan'} = Bugzilla::Testopia::TestPlan->new({}); + $vars->{'title'} = "Search For Test Plans"; + $vars->{'versions'} = get_searchable_objects('versions'); + } + elsif ($tab eq 'run'){ + $vars->{'title'} = "Search For Test Runs"; + $vars->{'run'} = Bugzilla::Testopia::TestRun->new({});; + $vars->{'versions'} = get_searchable_objects('versions'); + $vars->{'milestones'} = get_searchable_objects('milestones'); + $vars->{'builds'} = get_searchable_objects('builds'); + $vars->{'environments'} = get_searchable_objects('environments'); + } + elsif ($tab eq 'environment'){ + $vars->{'title'} = "Search For Test Run Environments"; + $vars->{'classifications'} = Bugzilla->user->get_selectable_classifications; + $vars->{'products'} = Bugzilla->user->get_selectable_products; + $vars->{'env'} = Bugzilla::Testopia::Environment->new({}); + } + elsif ($tab eq 'case_run'){ + $vars->{'title'} = "Search For Test Case-Runs"; + $vars->{'case'} = Bugzilla::Testopia::TestCase->new({}); + $vars->{'run'} = Bugzilla::Testopia::TestRun->new({}); + $vars->{'caserun'} = Bugzilla::Testopia::TestCaseRun->new({}); + + $vars->{'versions'} = get_searchable_objects('versions'); + $vars->{'milestones'} = get_searchable_objects('milestones'); + $vars->{'builds'} = get_searchable_objects('builds'); + $vars->{'environments'} = get_searchable_objects('environments'); + $vars->{'components'} = get_searchable_objects('components'); + $vars->{'categories'} = get_searchable_objects('categories'); + $vars->{'bug_status'} = convert_bugzilla_fields('bug_status'); + $vars->{'rep_platform'} = convert_bugzilla_fields('rep_platform'); + $vars->{'op_sys'} = convert_bugzilla_fields('op_sys'); + $vars->{'bug_priority'} = convert_bugzilla_fields('priority'); + $vars->{'bug_severity'} = convert_bugzilla_fields('bug_severity'); + $vars->{'resolution'} = convert_bugzilla_fields('resolution'); + } + elsif ($tab eq 'case') { + $tab = 'case'; + my $case = Bugzilla::Testopia::TestCase->new({ 'case_id' => 0 }); + $vars->{'title'} = "Search For Test Cases"; + $vars->{'case'} = $case; + $vars->{'components'} = get_searchable_objects('components'); + $vars->{'categories'} = get_searchable_objects('categories'); + $vars->{'bug_status'} = convert_bugzilla_fields('bug_status'); + $vars->{'rep_platform'} = convert_bugzilla_fields('rep_platform'); + $vars->{'op_sys'} = convert_bugzilla_fields('op_sys'); + $vars->{'bug_priority'} = convert_bugzilla_fields('priority'); + $vars->{'bug_severity'} = convert_bugzilla_fields('bug_severity'); + $vars->{'resolution'} = convert_bugzilla_fields('resolution'); + } + else{ + print "Location: tr_show_product.cgi?search=1 \n\n"; + exit; + } + print $cgi->header; + $vars->{'report'} = $cgi->param('report'); + $vars->{'current_tab'} = $tab; + $template->process("testopia/search/advanced.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} diff --git a/tr_quicksearch.cgi b/tr_quicksearch.cgi new file mode 100755 index 0000000..806a3d3 --- /dev/null +++ b/tr_quicksearch.cgi @@ -0,0 +1,448 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Classification; +use Bugzilla::Testopia::Constants; + +use JSON; + +use vars qw($vars); + +Bugzilla->login(LOGIN_REQUIRED); + +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +my $action = $cgi->param('action') || ''; +my $term = trim($cgi->param('searchstr')) || ''; + +push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; + +# Quicksearch allows a user to look up any of the major objects in Testopia +# using a simple prefix. For instance, e:linux will search for all environments +# with linux in the name or tr 33 will bring up Test Run 33. +# If only one is returned, we jump to the appropriate page, otherwise +# we display the list. + +# If we have a term we are using this quicksearch +if ($term){ + SWITCH: for ($term){ + /^(tag)?[\s:-]+(.*)$/i && do{ + my $text = trim($2); + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_tags.cgi?tag=" . $text . "\n\n"; + last SWITCH; + }; + /^(plan|TP|p)?[\s:-]+(.*)$/i && do{ + my $text = trim($2); + if ($text =~ /^\d+$/){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_plan.cgi?plan_id=" . $text . "\n\n"; + } + else{ + $cgi->param('current_tab', 'plan'); + $cgi->param('name_type', 'anywordssubstr'); + $cgi->param('name', $text); + + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('plan', 'tr_list_plans.cgi', $cgi, undef, $search->query); + if ($table->list_count == 1){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_plan.cgi?plan_id=" . ${$table->list}[0]->id . "\n\n"; + } + else{ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_list_plans.cgi?" . $table->get_query_part . "\n\n"; + } + + } + last SWITCH; + }; + /^(run|TR|r)?[\s:-]+(.*)$/i && do{ + my $text = trim($2); + if ($text =~ /^\d+$/){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_run.cgi?run_id=" . $text . "\n\n"; + } + else{ + $cgi->param('current_tab', 'run'); + $cgi->param('summary_type', 'anywordssubstr'); + $cgi->param('summary', $text); + + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('run', 'tr_list_runs.cgi', $cgi, undef, $search->query); + if ($table->list_count == 1){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_run.cgi?run_id=" . ${$table->list}[0]->id . "\n\n"; + } + else{ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_list_runs.cgi?" . $table->get_query_part . "\n\n"; + } + + } + last SWITCH; + }; + /^(environment|TE|e|env)?[\s:-]+(.*)$/i && do{ + my $text = trim($2); + if ($text =~ /^\d+$/){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_environments.cgi?env_id=" . $text . "\n\n"; + } + else{ + $cgi->param('current_tab', 'environment'); + $cgi->param('name_type', 'anywordssubstr'); + $cgi->param('name', $text); + + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('environment', 'tr_list_runs.cgi', $cgi, undef, $search->query); + if ($table->list_count == 1){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_environments.cgi?env_id=" . ${$table->list}[0]->id . "\n\n"; + } + else{ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_list_environments.cgi?" . $table->get_query_part . "\n\n"; + } + + } + last SWITCH; + }; + do{ + $term =~ s/^(case|TC|c)?[\s:-]+(.*)$/$2/gi; + if ($term =~ /^\d+$/){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_case.cgi?case_id=" . $term . "\n\n"; + } + else{ + $cgi->param('current_tab', 'case'); + $cgi->param('summary_type', 'anywordssubstr'); + $cgi->param('summary', $term); + + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case', 'tr_list_cases.cgi', $cgi, undef, $search->query); + if ($table->list_count == 1){ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_show_case.cgi?case_id=" . ${$table->list}[0]->id . "\n\n"; + } + else{ + print "Location: " . Bugzilla->params->{'urlbase'} . "tr_list_cases.cgi?" . $table->get_query_part . "\n\n"; + } + + } + }; + } + +} +############ +### Ajax ### +############ + +# This is where we lookup items typed into Dojo combo boxes +else{ + print $cgi->header; + +# Environment Lookup + if ($action eq 'getenvironments'){ + my $search = $cgi->param('search'); + my $prod_ids = $cgi->param('prod_id'); + trick_taint($search); + my @ids; + foreach my $id (split(',', $prod_ids)){ + push @ids, $id if detaint_natural($id); + } + unless (scalar @ids > 0){ + print "{}"; + exit; + } + $prod_ids = join(',', @ids); + + $search = "%$search%"; + my $dbh = Bugzilla->dbh; + + # The order of name and environment are important in the select statment. + # JSON will convert this to an array of arrays which Dojo will interpret + # as a select list in the ComboBox widget. + my $ref; + + if ($prod_ids){ + $ref = $dbh->selectall_arrayref( + "SELECT test_environments.name AS name, test_environments.environment_id + FROM test_environments + WHERE name like ? AND product_id IN($prod_ids) AND isactive = 1 + ORDER BY name", + undef, ($search)); + } + else{ + $ref = $dbh->selectall_arrayref( + "SELECT name, environment_id + FROM test_environments + WHERE name like ? AND isactive = 1 + ORDER BY name + LIMIT 20", + undef, ($search)); + } + print"{environments:"; + print to_json($ref); + print "}"; + } +# user lookup + elsif ($action eq 'getuser'){ + my $search = $cgi->param('search'); + my $start = $cgi->param('start'); + my $limit = 20; + detaint_natural($start) || exit; + exit if ($search eq ''); + $search = "%$search%"; + trick_taint($search); + my $dbh = Bugzilla->dbh; + + my $countquery = "SELECT COUNT(DISTINCT login_name) "; + my $query = "SELECT login_name, realname,"; + my $qbody = ''; + + if (Bugzilla->params->{'usevisibilitygroups'}) { + $query .= " COUNT(group_id) "; + } else { + $query .= " 1 "; + } + $qbody .= "FROM profiles "; + if (Bugzilla->params->{'usevisibilitygroups'}) { + $qbody .= "LEFT JOIN user_group_map " . + "ON user_group_map.user_id = userid AND isbless = 0 " . + "AND group_id IN(" . + join(', ', (-1, @{Bugzilla->user->visible_groups_inherited})) . ")"; + } + $qbody .= " WHERE disabledtext = '' AND (login_name LIKE ? OR realname LIKE ?) "; + + + $countquery .= $qbody; + + $query .= $qbody; + $query .= $dbh->sql_group_by('userid', 'login_name, realname'); + $query .= " ORDER BY login_name LIMIT $limit OFFSET $start"; + + my ($total) = $dbh->selectrow_array($countquery,undef,($search,$search)); + + my $sth = $dbh->prepare($query); + $sth->execute($search,$search); + + my @userlist; + while (my($login, $name, $visible) = $sth->fetchrow_array) { + if ($visible){ + push @userlist, { + 'id' => $login, 'name' => $name + }; + } + } + print "{'total':$total,'users':"; + print to_json(\@userlist); + print "}" + } +# Tag lookup + elsif ($action eq 'gettag'){ + my $search = $cgi->param('search'); + my @product_ids; + foreach my $id (split(",", $cgi->param('product_id'))){ + push @product_ids, $id if detaint_natural($id); + } + my $product_ids = join(",". @product_ids); + + trick_taint($search); + $search = "%$search%"; + my $dbh = Bugzilla->dbh; + my $ref; + my $run_id = $cgi->param('run_id'); + if ($product_ids){ + $ref = $dbh->selectall_arrayref( + "SELECT tag_name, test_tags.tag_id + FROM test_tags + INNER JOIN test_case_tags ON test_tags.tag_id = test_case_tags.tag_id + INNER JOIN test_cases on test_cases.case_id = test_case_tags.case_id + INNER JOIN test_case_plans on test_case_plans.case_id = test_cases.case_id + INNER JOIN test_plans ON test_plans.plan_id = test_case_plans.plan_id + WHERE tag_name like ? AND test_plans.product_id IN ($product_ids) + UNION SELECT tag_name, test_tags.tag_id + FROM test_tags + INNER JOIN test_plan_tags ON test_plan_tags.tag_id = test_tags.tag_id + INNER JOIN test_plans ON test_plan_tags.plan_id = test_plans.plan_id + WHERE tag_name like ? AND test_plans.product_id IN ($product_ids) + UNION SELECT tag_name, test_tags.tag_id + FROM test_tags + INNER JOIN test_run_tags ON test_run_tags.tag_id = test_tags.tag_id + INNER JOIN test_runs ON test_runs.run_id = test_run_tags.run_id + INNER JOIN test_plans ON test_plans.plan_id = test_runs.plan_id + WHERE tag_name like ? AND test_plans.product_id IN ($product_ids) + ORDER BY tag_name", + {'Slice' =>{}}, ($search,$search,$search)); + } + else { + $ref = $dbh->selectall_arrayref( + "SELECT tag_name, tag_id + FROM test_tags + WHERE tag_name like ? + ORDER BY tag_name + LIMIT 20", + {'Slice' =>{}}, $search); + } + print "{'tags':" . to_json($ref) . "}"; + } + elsif ($action eq 'getversions'){ + my $plan = Bugzilla::Testopia::TestPlan->new({}); + my $prod_id = $cgi->param("product_id"); + my @versions; + if ($prod_id && $prod_id == -1){ + # For update multiple from tr_list_plans + push @versions, {'id' => "--Do Not Change--", 'name' => "--Do Not Change--"}; + } + else{ + detaint_natural($prod_id); + my $prod = $plan->lookup_product($prod_id); + unless (Bugzilla->user->can_see_product($prod)){ + print '{ERROR:"You do not have permission to view this product"}'; + exit; + } + my $product = Bugzilla::Testopia::Product->new($prod_id); + @versions = @{$product->versions}; + } + my $json = new JSON; + + print "{versions:"; + print $json->encode(\@versions); + print "}"; + } + elsif ($action eq 'getmilestones'){ + my $product = Bugzilla::Testopia::Product->new($cgi->param("product_id")); + exit unless $product->canedit; + my $json = new JSON; + print "{milestones:"; + print $json->encode($product->milestones); + print "}"; + } + elsif ($action eq 'getplantypes'){ + my $plan = Bugzilla::Testopia::TestPlan->new({}); + my $json = new JSON; + print "{types:"; + print $json->encode($plan->get_plan_types()); + print "}"; + } + elsif ($action eq 'getpriorities'){ + my $plan = Bugzilla::Testopia::TestCase->new({}); + my $json = new JSON; + print "{priorities:"; + print $json->encode($plan->get_priority_list()); + print "}"; + } + elsif ($action eq 'getcasestatus'){ + my $plan = Bugzilla::Testopia::TestCase->new({}); + my $json = new JSON; + print "{statuses:"; + print $json->encode($plan->get_status_list()); + print "}"; + } + elsif ($action eq 'getcaserunstatus'){ + my $plan = Bugzilla::Testopia::TestCaseRun->new({}); + my $json = new JSON; + print "{statuses:"; + print $json->encode($plan->get_status_list()); + print "}"; + } + + elsif ($action eq 'getproducts'){ + my $products; + my $json = new JSON; + + if ($cgi->param('class_id')){ + my $class = Bugzilla::Testopia::Classification->new($cgi->param('class_id')); + $products = $class->user_visible_products; + } + else{ + $products = Bugzilla->user->get_selectable_products; + } + my @prods; + foreach my $p (@$products){ + push @prods, {name => $p->name, id => $p->id}; + } + print "{products:" . $json->encode(\@prods) . "}"; + } + + elsif ($action eq 'getclassificationstree'){ + my $node = $cgi->param('node'); + if ($node && $node ne 'classes'){ + $node =~ s/\D*//; + my @products; + my $classification = Bugzilla::Testopia::Classification->new($node); + foreach my $p (@{$classification->user_visible_products}){ + push @products, { + id => $p->id, + text => $p->name, + leaf => JSON::true, + attributes =>{ + defaultmilestone => $p->default_milestone, + canedit => $p->canedit ? JSON::true : JSON::false + }}; + } + my $json = new JSON; + print $json->encode(\@products); + exit; + } + my @classifications; + foreach my $c (@{Bugzilla->user->get_selectable_classifications}){ + push @classifications, {id => "c" . $c->id, text => $c->name, leaf => scalar @{$c->products} > 0 ? JSON::false : JSON::true}; + } + my $json = new JSON; + print $json->encode(\@classifications); + } + # For use in new_case and show_case since new_plan does not require an id + elsif ($action eq 'getcomponents'){ + my $plan = Bugzilla::Testopia::TestPlan->new({}); + my $product_id = $cgi->param('product_id'); + + detaint_natural($product_id); + my $prod = $plan->lookup_product($product_id); + unless (Bugzilla->user->can_see_product($prod)){ + print '{ERROR:"You do not have permission to view this product"}'; + exit; + } + my $product = Bugzilla::Testopia::Product->new($product_id); + + my @comps; + foreach my $c (@{$product->components}){ + push @comps, {'id' => $c->id, 'name' => $c->name, 'qa_contact' => $c->default_qa_contact->login}; + } + my $json = new JSON; + print "{'components':". $json->encode(\@comps) ."}"; + exit; + } + elsif ($action eq 'get_action'){ + print Bugzilla->params->{'new-case-action-template'}; + } + elsif ($action eq 'get_effect'){ + print Bugzilla->params->{'new-case-results-template'}; + + } + +# If neither is true above, display the quicksearch form and explanation. + else{ + $template->process("testopia/quicksearch.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + } +} diff --git a/tr_run_reports.cgi b/tr_run_reports.cgi new file mode 100644 index 0000000..cee0d37 --- /dev/null +++ b/tr_run_reports.cgi @@ -0,0 +1,379 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::User; + +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::Report; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Search; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); + +sub get_runs { + my ($plan_ids, $run_ids) = @_; + my @runs; + foreach my $g (@$plan_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestPlan->new($id); + push @runs, @{$obj->test_runs} if $obj && $obj->canview; + } + } + foreach my $g (@$run_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestRun->new($id); + push @runs, $obj if $obj && $obj->canview; + } + } + + unless (scalar @runs){ + print "No runs found"; + exit; + } + + my @run_ids; + foreach my $r (@runs){ + push @run_ids, $r->id; + } + return (\@runs,\@run_ids); +} + +my $type = $cgi->param('type') || ''; +$vars->{'qname'} = $cgi->param('qname'); + +if ($type eq 'completion'){ + print $cgi->header; + my $dbh = Bugzilla->dbh; + my @r = $cgi->param('run_ids'); + my @p = $cgi->param('plan_ids'); + + my ($runs, $run_ids) = get_runs(\@p, \@r); + my @runs = @$runs; + my @run_ids = @$run_ids; + + my $bugs = $dbh->selectcol_arrayref(" + SELECT DISTINCT tcb.bug_id + FROM test_case_bugs AS tcb + INNER JOIN test_case_runs AS tcr ON tcr.case_run_id = tcb.case_run_id + INNER JOIN bugs on tcb.bug_id = bugs.bug_id + INNER JOIN test_case_run_status AS tcrs ON tcr.case_run_status_id = tcrs.case_run_status_id + WHERE tcr.run_id in (" . join (',',@run_ids) . ") AND tcr.iscurrent = 1", + {"Slice" =>{}}); + + my $total = $runs[0]->case_run_count(undef, \@runs); + my $passed = $runs[0]->case_run_count(PASSED, \@runs); + my $failed = $runs[0]->case_run_count(FAILED, \@runs); + my $blocked = $runs[0]->case_run_count(BLOCKED, \@runs); + + my $completed = $passed + $failed + $blocked; + my $unfinished = $total - $completed; + + $vars->{'total'} = $total; + $vars->{'completed'} = $completed; + $vars->{'uncompleted'} = $unfinished; + $vars->{'passed'} = $passed; + $vars->{'failed'} = $failed; + $vars->{'blocked'} = $blocked; + + $vars->{'percent_completed'} = calculate_percent($total, $completed); + $vars->{'percent_passed'} = calculate_percent($completed, $passed); + $vars->{'percent_failed'} = calculate_percent($completed, $failed); + $vars->{'percent_blocked'} = calculate_percent($completed, $blocked); + $vars->{'percent_idle'} = calculate_percent($total, $unfinished); + + $vars->{'runs'} = join(',',@run_ids); + $vars->{'plans'} = join(',',@p); + $vars->{'bugs'} = join(',',@$bugs); + $vars->{'bug_count'} = scalar @$bugs; + $vars->{'run_count'} = scalar @run_ids; + + $template->process("testopia/reports/completion.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} +elsif ($type eq 'status'){ + print $cgi->header; + my $dbh = Bugzilla->dbh; + my @r = $cgi->param('run_ids'); + my @p = $cgi->param('plan_ids'); + + my ($runs, $run_ids) = get_runs(\@p, \@r); + my @runs = @$runs; + my @run_ids = @$run_ids; + + $vars->{'total'} = $runs[0]->case_run_count(undef, \@runs); + $vars->{'passed'} = $runs[0]->case_run_count(PASSED, \@runs); + $vars->{'failed'} = $runs[0]->case_run_count(FAILED, \@runs); + $vars->{'blocked'} = $runs[0]->case_run_count(BLOCKED, \@runs); + $vars->{'idle'} = $runs[0]->case_run_count(IDLE, \@runs); + $vars->{'running'} = $runs[0]->case_run_count(RUNNING, \@runs); + $vars->{'paused'} = $runs[0]->case_run_count(PAUSED, \@runs); + $vars->{'error'} = $runs[0]->case_run_count(ERROR, \@runs); + + $vars->{'runs'} = join(',',@run_ids); + $vars->{'plans'} = join(',',@p); + $vars->{'run_count'} = scalar @run_ids; + + $template->process("testopia/reports/status.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} +elsif ($type eq 'execution'){ + print $cgi->header; + my $dbh = Bugzilla->dbh; + my @run_ids = $cgi->param('run_ids'); + my @plan_ids = $cgi->param('plan_ids'); + my @runs; + + foreach my $g (@plan_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestPlan->new($id); + push @runs, @{$obj->test_runs} if $obj && $obj->canview; + } + } + foreach my $g (@run_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestRun->new($id); + push @runs, $obj if $obj && $obj->canview; + } + } + + unless (scalar @runs){ + print "No runs found"; + exit; + } + + @run_ids = (); + foreach my $r (@runs){ + push @run_ids, $r->id; + } + my $chfieldfrom = trim(lc($cgi->param('chfieldfrom'))) || ''; + my $chfieldto = trim(lc($cgi->param('chfieldto'))) || ''; + my $tester; + if ($cgi->param('tester')){ + $tester = login_to_id(trim($cgi->param('tester')), 'THROW_ERROR'); + } + + trick_taint($chfieldfrom); + trick_taint($chfieldto); + my $sql_chfrom = Bugzilla::Testopia::Search::SqlifyDate($chfieldfrom); + my $sql_chto = Bugzilla::Testopia::Search::SqlifyDate($chfieldto); + + my $total = $runs[0]->case_run_count_by_date($sql_chfrom, $sql_chto, undef, $tester, \@runs); + my $passed = $runs[0]->case_run_count_by_date($sql_chfrom, $sql_chto, PASSED, $tester, \@runs); + my $failed = $runs[0]->case_run_count_by_date($sql_chfrom, $sql_chto, FAILED, $tester, \@runs); + my $blocked = $runs[0]->case_run_count_by_date($sql_chfrom, $sql_chto, BLOCKED, $tester, \@runs); + + $vars->{'total'} = $total; + $vars->{'passed'} = $passed; + $vars->{'failed'} = $failed; + $vars->{'blocked'} = $blocked; + $vars->{'closed_from'} = $chfieldfrom; + $vars->{'closed_to'} = $chfieldto; + $vars->{'closed_from_converted'} = $sql_chfrom; + $vars->{'closed_to_converted'} = $sql_chto; + $vars->{'runs'} = \@run_ids; + $vars->{'plans'} = \@plan_ids; + + $template->process("testopia/reports/execution.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + + exit; + +} +elsif ($type eq 'bar'){ + + $vars->{'total'} = $cgi->param('t'); +# $vars->{'data'} = [ +# ["Total", "Completed", "Passed", "Failed", "Blocked"], +# [ $cgi->param('t'), $cgi->param('c'), $cgi->param('p'), $cgi->param('f'), $cgi->param('b') ], +# ]; + + $vars->{'colors'} = (['#B8A0D2', '#56e871', '#ed3f58','#e17a56']); + $vars->{'legend'} = ["Complete", "PASSED", "FAILED", "BLOCKED"]; + $vars->{'data'} = [ + ["CASES"], + [$cgi->param('c')], + [$cgi->param('p')], + [$cgi->param('f')], + [$cgi->param('b')], + ]; + + print $cgi->header; + $template->process("testopia/reports/completion.png.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + +} +elsif ($type eq 'bug'){ + print $cgi->header; + my @run_ids = $cgi->param('run_ids'); + my @plan_ids = $cgi->param('plan_ids'); + my @runs; + my $dbh = Bugzilla->dbh; + + foreach my $g (@plan_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestPlan->new($id); + push @runs, @{$obj->test_runs} if $obj && $obj->canview; + } + } + foreach my $g (@run_ids){ + foreach my $id (split(',', $g)){ + my $obj = Bugzilla::Testopia::TestRun->new($id); + push @runs, $obj if $obj && $obj->canview; + } + } + + unless (scalar @runs){ + print "No runs found"; + exit; + } + my @ids; + foreach my $r (@runs){ + push @ids, $r->id; + } + my $ref = $dbh->selectall_arrayref(" + SELECT DISTINCT tcb.bug_id, bugs.bug_status, bugs.bug_severity, tcr.run_id, tcr.case_id, tcrs.name AS case_status + FROM test_case_bugs AS tcb + INNER JOIN test_case_runs AS tcr ON tcr.case_run_id = tcb.case_run_id + INNER JOIN bugs on tcb.bug_id = bugs.bug_id + INNER JOIN test_case_run_status AS tcrs ON tcr.case_run_status_id = tcrs.case_run_status_id + WHERE tcr.run_id in (" . join (',',@ids) . ") AND tcr.iscurrent = 1", + {"Slice" =>{}}); + + my $json = new JSON; + print "{Result:"; + print $json->encode($ref); + print "}"; + exit; +} + +elsif ($type eq 'bug_grid'){ + $vars->{'runs'} = $cgi->param('run_ids'); + $vars->{'plans'} = $cgi->param('plan_ids'); + $vars->{'stripheader'} = 1 if $cgi->param('noheader'); + $vars->{'uid'} = rand(10000); + + print $cgi->header; + $template->process("testopia/reports/bug-count.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +elsif ($type eq 'priority'){ + print $cgi->header; + my $dbh = Bugzilla->dbh; + my @r = $cgi->param('run_ids'); + my @plans = $cgi->param('plan_ids'); + + my ($runs, $run_ids) = get_runs(\@plans, \@r); + my @runs = @$runs; + my @run_ids = @$run_ids; + my $priorities; + + foreach my $p (@{$dbh->selectall_arrayref("SELECT id, value FROM priority")}){ + $priorities->{$p->[1]}->{'total'} = $runs[0]->case_run_count_by_priority($p->[0], undef, \@runs); + $priorities->{$p->[1]}->{'passed'} = $runs[0]->case_run_count_by_priority($p->[0], PASSED, \@runs); + $priorities->{$p->[1]}->{'failed'} = $runs[0]->case_run_count_by_priority($p->[0], FAILED, \@runs); + $priorities->{$p->[1]}->{'blocked'} = $runs[0]->case_run_count_by_priority($p->[0], BLOCKED, \@runs); + $priorities->{$p->[1]}->{'idle'} = $runs[0]->case_run_count_by_priority($p->[0], IDLE, \@runs); + $priorities->{$p->[1]}->{'running'} = $runs[0]->case_run_count_by_priority($p->[0], RUNNING, \@runs); + $priorities->{$p->[1]}->{'paused'} = $runs[0]->case_run_count_by_priority($p->[0], PAUSED, \@runs); + $priorities->{$p->[1]}->{'error'} = $runs[0]->case_run_count_by_priority($p->[0], ERROR, \@runs); + } + $vars->{'priorities'} = $priorities; + $vars->{'runs'} = join(',',@run_ids); + $vars->{'plans'} = join(',',@plans); + $vars->{'run_count'} = scalar @run_ids; + + $template->process("testopia/reports/priority-breakdown.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + +} + +$cgi->param('current_tab', 'run'); +$cgi->param('viewall', 1); +my $report = Bugzilla::Testopia::Report->new('run', 'tr_list_runs.cgi', $cgi); +$vars->{'report'} = $report; + +### From Bugzilla report.cgi by Gervase Markham +my $formatparam = $cgi->param('format'); +my $report_action = $cgi->param('report_action'); +if ($report_action eq "data") { + # So which template are we using? If action is "wrap", we will be using + # no format (it gets passed through to be the format of the actual data), + # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else). + # report.html.tmpl produces an HTML framework for either tables of HTML + # data, or images generated by calling report.cgi again with action as + # "plot". + $formatparam =~ s/[^a-zA-Z\-]//g; + trick_taint($formatparam); + $vars->{'format'} = $formatparam; + $formatparam = ''; +} +elsif ($report_action eq "plot") { + # If action is "plot", we will be using a format as normal (pie, bar etc.) + # and a ctype as normal (currently only png.) + $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0; + $vars->{'x_labels_vertical'} = $cgi->param('x_labels_vertical') ? 1 : 0; + $vars->{'data'} = $report->{'image_data'}; +} +else { + ThrowCodeError("unknown_action", {action => $cgi->param('report_action')}); +} + +my $format = $template->get_format("testopia/reports/report", $formatparam, + scalar($cgi->param('ctype'))); + +my @time = localtime(time()); +my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; +my $filename = "report-" . $date . ".$format->{extension}"; + +my $disp = "inline"; +# We set CSV files to be downloaded, as they are designed for importing +# into other programs. +if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; +} + +print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + +$vars->{'time'} = $date; +$template->process("$format->{'template'}", $vars) + || ThrowTemplateError($template->error()); + +exit; + diff --git a/tr_show_case.cgi b/tr_show_case.cgi new file mode 100755 index 0000000..178a03b --- /dev/null +++ b/tr_show_case.cgi @@ -0,0 +1,75 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Tyler Peterson + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; +my $case_id = trim(Bugzilla->cgi->param('case_id')) || ''; + +unless ($case_id){ + print $cgi->header(); + $template->process("testopia/case/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +my $case = Bugzilla::Testopia::TestCase->new($case_id); +ThrowUserError("invalid-test-id-non-existent", {'type' => 'case', id => $case_id}) unless $case; +ThrowUserError("testopia-permission-denied", {'object' => $case}) unless $case->canview; + +my $format = $template->get_format("testopia/case/show", scalar $cgi->param('format'), scalar $cgi->param('ctype')); +my $disp = "inline"; +# We set CSV files to be downloaded, as they are designed for importing +# into other programs. +if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + $vars->{'displaycolumns'} = \@Bugzilla::Testopia::Constants::TESTCASE_EXPORT; +} + +$vars->{'table'} = Bugzilla::Testopia::Table->new('case', 'tr_list_cases.cgi', $cgi); + +# Suggest a name for the file if the user wants to save it as a file. +my @time = localtime(time()); +my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; +my $filename = "testcase-$case_id-$date.$format->{extension}"; +print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + +$vars->{'case'} = $case; +$template->process($format->{'template'}, $vars) || + ThrowTemplateError($template->error()); + diff --git a/tr_show_plan.cgi b/tr_show_plan.cgi new file mode 100755 index 0000000..502ae50 --- /dev/null +++ b/tr_show_plan.cgi @@ -0,0 +1,81 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Maciej Maczynski +# Ed Fuentetaja +# Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; +use Bugzilla::Testopia::TestPlan; + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +my $plan_id = trim(Bugzilla->cgi->param('plan_id')); + +unless ($plan_id){ + print $cgi->header; + $vars->{'form_action'} = 'tr_show_plan.cgi'; + $template->process("testopia/plan/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); +ThrowUserError("invalid-test-id-non-existent", {'type' => 'plan', id => $plan_id}) unless $plan; +ThrowUserError("testopia-permission-denied", {'object' => $plan}) unless $plan->canview; + +$vars->{'table'} = Bugzilla::Testopia::Table->new('plan', 'tr_list_plans.cgi', $cgi); +$vars->{'printdoc'} = 1 if ($cgi->param('ctype') eq 'print'); +$vars->{'plan'} = $plan; + +my $format = $template->get_format("testopia/plan/show", scalar $cgi->param('format'), scalar $cgi->param('ctype')); +my $disp = "inline"; +# We set CSV files to be downloaded, as they are designed for importing +# into other programs. +if ( $format->{'extension'} eq "csv" || $format->{'extension'} eq "xml" ){ + $disp = "attachment"; + $vars->{'displaycolumns'} = \@Bugzilla::Testopia::Constants::TESTCASE_EXPORT; + $vars->{'table'} = $plan->test_cases; +} + +# Suggest a name for the file if the user wants to save it as a file. +my @time = localtime(time()); +my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; +my $filename = "testcases-$date.$format->{extension}"; +print $cgi->header(-type => $format->{'ctype'}, + -content_disposition => "$disp; filename=$filename"); + +$vars->{'percentage'} = \&percentage; + +$template->process($format->{'template'}, $vars) || + ThrowTemplateError($template->error()); diff --git a/tr_show_product.cgi b/tr_show_product.cgi new file mode 100755 index 0000000..7f6765b --- /dev/null +++ b/tr_show_product.cgi @@ -0,0 +1,82 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; +my $cgi = Bugzilla->cgi; + +Bugzilla->login(LOGIN_REQUIRED); + +print $cgi->header; + +############################################################################### +# tr_show_product.cgi +# Displays product level information including builds, categories, environments +# and tags as well as provides product level reports. +# +# INTERFACE: +# product_id: product to display +# action: +# +################################################################################ +my $product; +my $pid = $cgi->param('product_id') || $cgi->cookie('TESTOPIA_PRODUCT_ID') || 0; +if ($pid){ + $product = Bugzilla::Testopia::Product->new($pid); + if ($product){ + ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canview; + } + $vars->{'product'} = $product; + $vars->{'cookiepath'} = Bugzilla->params->{'cookiepath'}; +} + +$cgi->param('current_tab', 'plan'); +$cgi->param('pagesize', $cgi->param('limit')); +$cgi->param('name_type','allwordssubstr'); +if (exists $cgi->{'start'} || exists $cgi->{param}->{'start'}){ + $cgi->param('page', $cgi->param('start') == 0 ? 0 : $cgi->param('start')/$cgi->param('limit')); +} + +my $search = Bugzilla::Testopia::Search->new($cgi); +my $table = Bugzilla::Testopia::Table->new('plan', 'tr_list_plans.cgi', $cgi, undef, $search->query); +my $action = $cgi->param('action') || ''; + +$vars->{'table'} = $table; +$vars->{'search'} = $cgi->param('search'); +$vars->{'case'} = Bugzilla::Testopia::TestCase->new({}); + +$vars->{'dashboard'} = $cgi->param('dashboard'); +$vars->{'userid'} = $cgi->param('userid'); + +$template->process("testopia/product/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); diff --git a/tr_show_run.cgi b/tr_show_run.cgi new file mode 100755 index 0000000..c0a6eae --- /dev/null +++ b/tr_show_run.cgi @@ -0,0 +1,60 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Test Runner System. +# +# The Initial Developer of the Original Code is Maciej Maczynski. +# Portions created by Maciej Maczynski are Copyright (C) 2001 +# Maciej Maczynski. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::Constants; + +my $vars = {}; +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + print $cgi->header; +local our $run_id = trim($cgi->param('run_id') || ''); + +unless ($run_id){ + $template->process("testopia/run/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} +my $run = Bugzilla::Testopia::TestRun->new($run_id); +ThrowUserError("invalid-test-id-non-existent", {'type' => 'run', id => $run_id}) unless $run; +ThrowUserError("testopia-permission-denied", {'object' => $run}) unless $run->canview; + +$vars->{'table'} = Bugzilla::Testopia::Table->new('run', 'tr_list_runs.cgi', $cgi); +$vars->{'caserun'} = Bugzilla::Testopia::TestCaseRun->new({}); +$vars->{'case'} = Bugzilla::Testopia::TestCase->new({}); +$vars->{'run'} = $run; + +$template->process("testopia/run/show.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + diff --git a/tr_tags.cgi b/tr_tags.cgi new file mode 100755 index 0000000..2d743d5 --- /dev/null +++ b/tr_tags.cgi @@ -0,0 +1,232 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::User; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestTag; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Constants; + +use JSON; + +Bugzilla->login(LOGIN_REQUIRED); + +local our $cgi = Bugzilla->cgi; +local our $vars = {}; +local our $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $type = $cgi->param('type'); + +$vars->{'type'} = $type; + +if ($action eq 'delete'){ + my $tag_id = $cgi->param('tagid'); + validate_test_id($tag_id, 'tag'); + my $tag = Bugzilla::Testopia::TestTag->new($tag_id); + ThrowUserError("testopia-no-delete", {'object' => $tag}) unless $tag->candelete; + $tag->obliterate; + $vars->{'tr_message'} = "Tag " . $tag->name . " deleted"; + display(); +} + +#################### +### Ajax Actions ### +#################### +elsif ($action eq 'addtag' || $action eq 'removetag'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + + my $obj; + foreach my $id (split(/,/, $cgi->param('id'))){ + if ($type eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($id); + } elsif ($type eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($id); + } elsif($type eq 'run'){ + $obj = Bugzilla::Testopia::TestRun->new($id); + } + + ThrowUserError("unknown-type", {type => $type}) unless ($obj); + ThrowUserError("testopia-read-only", {'object' => $obj}) unless $obj->canedit; + + if ($action eq 'addtag'){ + $obj->add_tag($cgi->param('tag')); + } + else { + foreach my $tag (split(',', $cgi->param('tag'))){ + trick_taint($tag); + $obj->remove_tag($tag); + } + } + } + if ($cgi->param('method')){ + $vars->{'tr_message'} = "Added tag " . $cgi->param('tag') . " To $type " . $obj->id; + $cgi->param($type.'_id', $obj->id); + display(); + exit; + } + + print "{success: true}"; + +} +elsif ($action eq 'gettags'){ + Bugzilla->error_mode(ERROR_MODE_AJAX); + my $json = new JSON; + my $tags; + if ($cgi->param('type') eq 'user'){ + $tags = get_user_tags(); + } + elsif ($cgi->param('type') eq 'product'){ + my $product = Bugzilla::Testopia::Product->new($cgi->param('product_id')); + $tags = $product->tags if $product->canedit; + } + else { + my $type = $cgi->param('type'); + my $id = $cgi->param('id'); + my $obj; + if ($type eq 'plan'){ + $obj = Bugzilla::Testopia::TestPlan->new($id); + } elsif ($type eq 'case'){ + $obj = Bugzilla::Testopia::TestCase->new($id); + } elsif($type eq 'run'){ + $obj = Bugzilla::Testopia::TestRun->new($id); + } + + ThrowUserError("unkown-type", {type => $type}) unless ($obj); + ThrowUserError("testopia-permission-denied", {'object' => $obj}) unless $obj->canview; + + $tags = $obj->tags; + } + + my $out = "{tags:["; + foreach my $tag (@$tags){ + $out .= $tag->TO_JSON . ','; + } + chop($out) if scalar @$tags; + print $out ."]}"; + +} +################### +### Body ### +################### +else { + display(); +} +################### +### Subroutines ### +################### + +sub display { + my $dbh = Bugzilla->dbh; + my @tags; + + if ($cgi->param('action') eq 'show_all' && Bugzilla->user->in_group('admin')){ + my $tags = $dbh->selectcol_arrayref( + "SELECT tag_id FROM test_tags + ORDER BY tag_name"); + foreach my $t (@{$tags}){ + push @tags, Bugzilla::Testopia::TestTag->new($t); + } + $vars->{'viewall'} = 1; + } + + + $vars->{'user_tags'} = get_user_tags(); + $vars->{'user_name'} = $cgi->param('user') ? $cgi->param('user') : Bugzilla->user->login; + + if ($cgi->param('case_id')){ + my $case_id = $cgi->param('case_id'); + detaint_natural($case_id); + $vars->{'case'} = Bugzilla::Testopia::TestCase->new($case_id); + } + if ($cgi->param('plan_id')){ + my $plan_id = $cgi->param('plan_id'); + detaint_natural($plan_id); + $vars->{'plan'} = Bugzilla::Testopia::TestPlan->new($plan_id); + } + if ($cgi->param('run_id')){ + my $run_id = $cgi->param('run_id'); + detaint_natural($run_id); + $vars->{'run'} = Bugzilla::Testopia::TestRun->new($run_id); + } + + my @products; + foreach my $id (split(",", $cgi->param('product'))){ + my $product = Bugzilla::Testopia::Product->new($id); + push @products, $product if Bugzilla->user->can_see_product($product->name); + } + $vars->{'products'} = \@products; + + my @tagids = split(/[\s,]/, $cgi->param('tag_id')); + + foreach my $id (@tagids){ + detaint_natural($id); + push @tags, Bugzilla::Testopia::TestTag->new($id); + } + + if ($cgi->param('tag')){ + my $name = trim($cgi->param('tag')); + trick_taint($name); + push @tags, Bugzilla::Testopia::TestTag->new($name); + } + + $vars->{'tags'} = \@tags; + $template->process("testopia/tag/show.html.tmpl", $vars) + || print $template->error(); + +} + +sub get_user_tags { + my $user; + my $dbh = Bugzilla->dbh; + $user = login_to_id($cgi->param('user')) if $cgi->param('user'); + + my $userid = $user ? $user : Bugzilla->user->id; + ThrowUserError("invalid_username", { name => $cgi->param('user') }) unless $userid; + my $user_tags = $dbh->selectcol_arrayref( + "(SELECT test_tags.tag_id, test_tags.tag_name AS name FROM test_case_tags + INNER JOIN test_tags ON test_case_tags.tag_id = test_tags.tag_id + WHERE userid = ?) + UNION (SELECT test_tags.tag_id, test_tags.tag_name AS name FROM test_plan_tags + INNER JOIN test_tags ON test_plan_tags.tag_id = test_tags.tag_id + WHERE userid = ?) + UNION (SELECT test_tags.tag_id, test_tags.tag_name AS name FROM test_run_tags + INNER JOIN test_tags ON test_run_tags.tag_id = test_tags.tag_id + WHERE userid = ?) + ORDER BY name", undef, ($userid, $userid, $userid)); + my @user_tags; + foreach my $id (@$user_tags){ + push @user_tags, Bugzilla::Testopia::TestTag->new($id); + } + return \@user_tags; +} \ No newline at end of file diff --git a/tr_xmlrpc.cgi b/tr_xmlrpc.cgi new file mode 100755 index 0000000..8bd8043 --- /dev/null +++ b/tr_xmlrpc.cgi @@ -0,0 +1,53 @@ +#!/usr/bin/perl -wT +####!/usr/bin/perl -d:ptkdb -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +sub BEGIN +{ + # For use with ptkdb. + $ENV{DISPLAY}=":0.0"; +} + +use strict; +use lib qw(. lib); + +use XMLRPC::Transport::HTTP; +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::WebService; + +Bugzilla->usage_mode(USAGE_MODE_WEBSERVICE); + +die 'Content-Type must be "text/xml" when using API' unless + $ENV{'CONTENT_TYPE'} eq 'text/xml'; + +my $dispatch = { 'TestPlan' => 'Bugzilla::WebService::Testopia::TestPlan', + 'TestCase' => 'Bugzilla::WebService::Testopia::TestCase', + 'TestRun' => 'Bugzilla::WebService::Testopia::TestRun', + 'TestCaseRun' => 'Bugzilla::WebService::Testopia::TestCaseRun', + 'Product' => 'Bugzilla::WebService::Testopia::Product', + 'Environment' => 'Bugzilla::WebService::Testopia::Environment', + 'Build' => 'Bugzilla::WebService::Testopia::Build', + 'Testopia' => 'Bugzilla::WebService::Testopia::Testopia', + 'User' => 'Bugzilla::WebService::User', + }; +my $response = Bugzilla::WebService::XMLRPC::Transport::HTTP::CGI + ->dispatch_with($dispatch) + ->on_action(sub { Bugzilla::WebService::handle_login($dispatch, @_) } ) + ->handle; + \ No newline at end of file From fbdf408d4b078550d950a0bf6565cfeec6dc5a4f Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Thu, 30 Jul 2009 20:44:45 +0000 Subject: [PATCH 02/13] Bug 476024 - Include the test plan name in the Product dashboard Test case tab. patch by maparent@miranda.com; r=ghendricks --- .project | 2 +- Bugzilla/Testopia/TestCase.pm | 2 ++ testopia/js/case.js | 8 +++++--- testopia/testopia.all.js | 8 +++++--- testopia/testopia.all.ycomp.js | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.project b/.project index 0b5ffe8..14d4ee9 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - testopia-1.0-2.22 + testopia-3.2-branch diff --git a/Bugzilla/Testopia/TestCase.pm b/Bugzilla/Testopia/TestCase.pm index 5a1d058..7af57b8 100644 --- a/Bugzilla/Testopia/TestCase.pm +++ b/Bugzilla/Testopia/TestCase.pm @@ -28,6 +28,7 @@ # # Contributor(s): Greg Hendricks # Jeff Dayley +# M-A Parent package Bugzilla::Testopia::TestCase; @@ -1673,6 +1674,7 @@ sub TO_JSON { $obj->{$field} = $self->{$field}; } + $obj->{'plan_name'} = ${$self->plans}[0]->name; $obj->{'run_count'} = $self->run_count; $obj->{'author_name'} = $self->author->name if $self->author; $obj->{'default_tester'} = $self->default_tester->name if $self->default_tester; diff --git a/testopia/js/case.js b/testopia/js/case.js index 744359f..a547e39 100644 --- a/testopia/js/case.js +++ b/testopia/js/case.js @@ -18,6 +18,7 @@ * Contributor(s): Greg Hendricks * Ryan Hamilton * Daniel Parker + * M-A Parent */ CasePanel = function(params,cfg){ @@ -102,8 +103,8 @@ CaseGrid = function(params, cfg){ {name: "product_id", mapping:"product_id"}, {name: "component", mapping:"component"}, {name: "modified", mapping:"modified"}, - {name: "isautomated", mapping:"isautomated"} - + {name: "isautomated", mapping:"isautomated"}, + {name: "plan_name", mapping:"plan_name"} ]}), remoteSort: true, sortInfo: {field: 'case_id', direction: "ASC"}, @@ -173,7 +174,8 @@ CaseGrid = function(params, cfg){ name: 'requirement' })) }, - {header: "Plan", width: 40, sortable: true, dataIndex: 'plan_id', hidden: true, renderer: tutil.plan_link, groupRenderer: function(v){return v;}}, + {header: "Plan", width: 40, sortable: true, dataIndex: 'plan_id', hidden: true, renderer: tutil.plan_link, + groupRenderer: function(v, u, r){return v + ': "' + r.get('plan_name') + '"';}}, {header: "Run Count", width: 40, sortable: false, dataIndex: 'run_count', hidden: true} ]; this.view = new Ext.grid.GroupingView({ diff --git a/testopia/testopia.all.js b/testopia/testopia.all.js index 5008ed9..9f62fb2 100644 --- a/testopia/testopia.all.js +++ b/testopia/testopia.all.js @@ -3036,6 +3036,7 @@ PlanClonePopup = function(plan){ * Contributor(s): Greg Hendricks * Ryan Hamilton * Daniel Parker + * M-A Parent */ CasePanel = function(params,cfg){ @@ -3120,8 +3121,8 @@ CaseGrid = function(params, cfg){ {name: "product_id", mapping:"product_id"}, {name: "component", mapping:"component"}, {name: "modified", mapping:"modified"}, - {name: "isautomated", mapping:"isautomated"} - + {name: "isautomated", mapping:"isautomated"}, + {name: "plan_name", mapping:"plan_name"} ]}), remoteSort: true, sortInfo: {field: 'case_id', direction: "ASC"}, @@ -3191,7 +3192,8 @@ CaseGrid = function(params, cfg){ name: 'requirement' })) }, - {header: "Plan", width: 40, sortable: true, dataIndex: 'plan_id', hidden: true, renderer: tutil.plan_link, groupRenderer: function(v){return v;}}, + {header: "Plan", width: 40, sortable: true, dataIndex: 'plan_id', hidden: true, renderer: tutil.plan_link, + groupRenderer: function(v, u, r){return v + ': "' + r.get('plan_name') + '"';}}, {header: "Run Count", width: 40, sortable: false, dataIndex: 'run_count', hidden: true} ]; this.view = new Ext.grid.GroupingView({ diff --git a/testopia/testopia.all.ycomp.js b/testopia/testopia.all.ycomp.js index a96fca0..57c9e1f 100644 --- a/testopia/testopia.all.ycomp.js +++ b/testopia/testopia.all.ycomp.js @@ -1 +1 @@ -ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return'
'+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E){return E}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file +ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file From 6c620759ec6ebd0a8ecdc77bd0ec37d52a171248 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Fri, 31 Jul 2009 21:50:16 +0000 Subject: [PATCH 03/13] Notes are getting truncated --- template/en/default/testopia/case/text.xml.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/en/default/testopia/case/text.xml.tmpl b/template/en/default/testopia/case/text.xml.tmpl index 9e1564b..ccdcc74 100644 --- a/template/en/default/testopia/case/text.xml.tmpl +++ b/template/en/default/testopia/case/text.xml.tmpl @@ -8,7 +8,7 @@ [% text.case_id FILTER xml %] [% text.author FILTER xml %] - [% text.notes FILTER xml %] + \ No newline at end of file From 74ab6c5c230889584b29ac657a53d5d7ee1cf130 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Mon, 3 Aug 2009 17:47:03 +0000 Subject: [PATCH 04/13] Tag cases with bugs that are fixed when the bug is updated. --- extensions/testopia/code/bug-end_of_update.pl | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 extensions/testopia/code/bug-end_of_update.pl diff --git a/extensions/testopia/code/bug-end_of_update.pl b/extensions/testopia/code/bug-end_of_update.pl new file mode 100644 index 0000000..8876ce7 --- /dev/null +++ b/extensions/testopia/code/bug-end_of_update.pl @@ -0,0 +1,56 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Example Plugin. +# +# The Initial Developer of the Original Code is Everything Solved, Inc. +# Portions created by Everything Solved are Copyright (C) 2008 +# Everything Solved, Inc. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +use Bugzilla::Status; +use Bugzilla::Testopia::TestCase; + +my $args = Bugzilla->hook_args; +my $bug = $args->{'bug'}; +my $timestamp = $args->{'timestamp'}; +my $changes = $args->{'changes'}; +my $dbh = Bugzilla->dbh; + +foreach my $field (keys %$changes) { + my $used_to_be = $changes->{$field}->[0]; + my $now_it_is = $changes->{$field}->[1]; +} + +my $tcrs = $dbh->selectcol_arrayref("SELECT case_id FROM test_case_bugs WHERE bug_id = ?", undef, $bug->id); + +my $status_message; +if (my $status_change = $changes->{'bug_status'}) { + my $old_status = new Bugzilla::Status({ name => $status_change->[0] }); + my $new_status = new Bugzilla::Status({ name => $status_change->[1] }); + if ($new_status->is_open && !$old_status->is_open) { + for my $tcr(@$tcrs) { + my $tc = Bugzilla::Testopia::TestCase->new($tcr); + $tc->remove_tag('BUGFIXED'); + } + } + if (!$new_status->is_open && $old_status->is_open) { + for my $tcr(@$tcrs) { + my $tc = Bugzilla::Testopia::TestCase->new($tcr); + $tc->add_tag('BUGFIXED'); + } + } +} From f6208beb21250caa545f860a5c09e9d13286bbd1 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Tue, 4 Aug 2009 17:57:17 +0000 Subject: [PATCH 05/13] Context menu deletion of tags is broken --- testopia/js/tags.js | 1 + testopia/testopia.all.js | 1 + testopia/testopia.all.ycomp.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/testopia/js/tags.js b/testopia/js/tags.js index ce92405..875fc0a 100644 --- a/testopia/js/tags.js +++ b/testopia/js/tags.js @@ -148,6 +148,7 @@ Ext.extend(TestopiaObjectTags, Ext.grid.GridPanel, { text: 'Remove Selected Tags', icon: 'testopia/img/delete.png', iconCls: 'img_button_16x', + obj_id: this.obj_id, handler: this.remove },{ text: 'Refresh List', diff --git a/testopia/testopia.all.js b/testopia/testopia.all.js index 9f62fb2..c27cb1b 100644 --- a/testopia/testopia.all.js +++ b/testopia/testopia.all.js @@ -9340,6 +9340,7 @@ Ext.extend(TestopiaObjectTags, Ext.grid.GridPanel, { text: 'Remove Selected Tags', icon: 'testopia/img/delete.png', iconCls: 'img_button_16x', + obj_id: this.obj_id, handler: this.remove },{ text: 'Refresh List', diff --git a/testopia/testopia.all.ycomp.js b/testopia/testopia.all.ycomp.js index 57c9e1f..f23f2c7 100644 --- a/testopia/testopia.all.ycomp.js +++ b/testopia/testopia.all.ycomp.js @@ -1 +1 @@ -ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file +ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",obj_id:this.obj_id,handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file From 9bdf8a3f69e617fead194f63d53618691f5940d3 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 15:02:19 +0000 Subject: [PATCH 06/13] Category updates were broken on case runs. Don't delete cases with priorities are removed. --- extensions/testopia/code/db_schema-abstract_schema.pl | 2 +- extensions/testopia/code/install-update_db.pl | 2 ++ testopia/js/caserun.js | 2 +- testopia/testopia.all.js | 2 +- testopia/testopia.all.ycomp.js | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/testopia/code/db_schema-abstract_schema.pl b/extensions/testopia/code/db_schema-abstract_schema.pl index 46e16a9..e89cdcd 100644 --- a/extensions/testopia/code/db_schema-abstract_schema.pl +++ b/extensions/testopia/code/db_schema-abstract_schema.pl @@ -130,7 +130,7 @@ REFERENCES => { TABLE => 'priority', COLUMN => 'id', - DELETE => 'CASCADE' + DELETE => 'RESTRICT' } }, author_id => { diff --git a/extensions/testopia/code/install-update_db.pl b/extensions/testopia/code/install-update_db.pl index 6d57a38..c0c6226 100644 --- a/extensions/testopia/code/install-update_db.pl +++ b/extensions/testopia/code/install-update_db.pl @@ -61,6 +61,8 @@ sub testopiaUpdateDB { $dbh->bz_drop_table('test_plan_testers'); $dbh->bz_drop_table('test_plan_group_map'); $dbh->bz_drop_column('test_plans', 'editor_id'); + + $dbh->bz_drop_fk('test_cases', 'priority_id') if $dbh->bz_column_info('test_cases','priority_id')->{REFERENCES}->{DELETE} eq 'CASCADE'; $dbh->bz_add_column('test_case_bugs', 'case_id', {TYPE => 'INT4'}); $dbh->bz_add_column('test_case_runs', 'environment_id', {TYPE => 'INT4', NOTNULL => 1}, 0); diff --git a/testopia/js/caserun.js b/testopia/js/caserun.js index a43fa71..21c6002 100755 --- a/testopia/js/caserun.js +++ b/testopia/js/caserun.js @@ -995,7 +995,7 @@ Ext.extend(CaseRunGrid, Ext.grid.EditorGridPanel, { myparams.priority = gevent.value; break; case 'category': - myparams.action = 'update_scategory'; + myparams.action = 'update_category'; myparams.category = gevent.value; break; } diff --git a/testopia/testopia.all.js b/testopia/testopia.all.js index c27cb1b..2ecf10c 100644 --- a/testopia/testopia.all.js +++ b/testopia/testopia.all.js @@ -5356,7 +5356,7 @@ Ext.extend(CaseRunGrid, Ext.grid.EditorGridPanel, { myparams.priority = gevent.value; break; case 'category': - myparams.action = 'update_scategory'; + myparams.action = 'update_category'; myparams.category = gevent.value; break; } diff --git a/testopia/testopia.all.ycomp.js b/testopia/testopia.all.ycomp.js index f23f2c7..648d553 100644 --- a/testopia/testopia.all.ycomp.js +++ b/testopia/testopia.all.ycomp.js @@ -1 +1 @@ -ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_scategory";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",obj_id:this.obj_id,handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file +ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_category";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",obj_id:this.obj_id,handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file From 67f47dc7018a3a634656dba75364da231372c0e6 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 15:36:26 +0000 Subject: [PATCH 07/13] Bug 480578 - Sorting search results modifies the results --- Bugzilla/Testopia/Search.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla/Testopia/Search.pm b/Bugzilla/Testopia/Search.pm index 9fe40a9..5bb0a97 100644 --- a/Bugzilla/Testopia/Search.pm +++ b/Bugzilla/Testopia/Search.pm @@ -459,7 +459,7 @@ sub init { if ($obj eq 'case_run'){ push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; } - push @supptables, "INNER JOIN test_case_components ON test_cases.case_id = test_case_components.case_id"; + push @supptables, "LEFT JOIN test_case_components ON test_cases.case_id = test_case_components.case_id"; push @supptables, "LEFT JOIN components AS comp_sort ON comp_sort.id = test_case_components.component_id"; push @orderby, 'comp_sort.name'; } From 688fda10c26bb80b42c5c6f1b416e7bc0fb83ad7 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 16:26:10 +0000 Subject: [PATCH 08/13] Bug 481140 - can not input any character while open search page a second time --- testopia/js/search.js | 4 ++++ testopia/testopia.all.js | 4 ++++ testopia/testopia.all.ycomp.js | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/testopia/js/search.js b/testopia/js/search.js index 6710e63..21efcd4 100644 --- a/testopia/js/search.js +++ b/testopia/js/search.js @@ -56,6 +56,10 @@ Testopia.Search.fillInForm = function(type, params, name){ }; SearchPopup = function(tab, params){ + if (Ext.getCmp('search_win')){ + Ext.getCmp('search_win').show(); + return; + } var win = new Ext.Window({ id: 'search_win', closable: true, diff --git a/testopia/testopia.all.js b/testopia/testopia.all.js index 2ecf10c..e69944b 100644 --- a/testopia/testopia.all.js +++ b/testopia/testopia.all.js @@ -8443,6 +8443,10 @@ Testopia.Search.fillInForm = function(type, params, name){ }; SearchPopup = function(tab, params){ + if (Ext.getCmp('search_win')){ + Ext.getCmp('search_win').show(); + return; + } var win = new Ext.Window({ id: 'search_win', closable: true, diff --git a/testopia/testopia.all.ycomp.js b/testopia/testopia.all.ycomp.js index 648d553..dbe4852 100644 --- a/testopia/testopia.all.ycomp.js +++ b/testopia/testopia.all.ycomp.js @@ -1 +1 @@ -ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_category";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",obj_id:this.obj_id,handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file +ATTACHMENT_DELETE_WARNING="You are about to remove the selected attachments. This cannot be undone. Continue?";CASE_CATEGORY_DELETE_WARNING="You are about to delete the selected test case category. Are you sure you want to continue?";CASE_DELETE_WARNING="You are about to delete the selected test cases including all children and history. This action cannot be undone. Are you sure you want to continue?";PLAN_DELETE_WARNING="You are about to delete the selected test plans including all children and history. This action cannot be undone. Are you sure you want to continue?";RUN_DELETE_WARNING="You are about to delete the selected test runs including all children and history. This action cannot be undone. Are you sure you want to continue?";CASERUN_DELETE_WARNING="You are about to remove the selected test cases from this run including all history. This action cannot be undone. Are you sure you want to continue?";ENVIRONMENT_DELETE_WARNING="You are about to delete the selected test environment including associated test case data. This action cannot be undone. Are you sure you want to continue?";Ext.form.TriggerField.override({afterRender:function(){Ext.form.TriggerField.superclass.afterRender.call(this);var A;if(Ext.isIE&&!this.hideTrigger&&this.el.getY()!=(A=this.trigger.getY())){this.el.position();this.el.setY(A)}}});Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date(new Date().getTime()+(1000*60*60*24*30))}));Ext.data.Connection.timeout=120000;Ext.Updater.defaults.timeout=120000;Ext.Ajax.timeout=120000;var Testopia={};Testopia.Util={};Testopia.Environment={};Ext.grid.CheckColumn=function(A){Ext.apply(this,A);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.grid.CheckColumn.prototype={init:function(A){this.grid=A;this.grid.on("render",function(){var B=this.grid.getView();B.mainBody.on("mousedown",this.onMouseDown,this)},this)},onMouseDown:function(D,C){if(C.className&&C.className.indexOf("x-grid3-cc-"+this.id)!=-1){D.stopEvent();var B=this.grid.getView().findRowIndex(C);var A=this.grid.store.getAt(B);A.set(this.dataIndex,!A.data[this.dataIndex])}},renderer:function(B,C,A){C.css+=" x-grid3-check-col-td";return'
 
'}};var imgButtonTpl=new Ext.Template('
');TestopiaUtil=function(){this.statusIcon=function(B){return''+B+''};this.caseLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.runLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.planLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.bugLink=function(G,B,F,C,D,E){if(E.isTreport===true){return''+G+""}return''+G+""};this.newRunPopup=function(C){var B=new Ext.Window({id:"newRun-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewRunForm(C)]});B.show(this)};this.newCaseForm=function(E,C,B){var D=new Ext.Window({id:"newcase-win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new NewCaseForm(E,C,B)]});D.show(this)};this.addCaseToRunPopup=function(C){var B=new Ext.Window({id:"add_case_to_run_win",closable:true,width:Ext.getBody().getViewSize().width-150,height:Ext.getBody().getViewSize().height-150,plain:true,shadow:false,layout:"fit",items:[new AddCaseToRunForm(C)]});B.show(this)};this.newPlanPopup=function(B){var C=new Ext.Window({id:"newplan-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new NewPlanForm(B)]});C.show(this)};addOption=function(B,C){try{B.add(C,null)}catch(D){B.add(C,B.length)}};lsearch=function(D,B){if(typeof B!="object"){if(B==D){return true}return false}for(var C in B){if(B[C]==D){return true}}return false};this.addOption=addOption;var A=function(H,C){var E=searchToJson(window.location.search);if(C){E.product=C}for(var D in H.selectTypes){if(typeof H.selectTypes[D]!="function"){try{document.getElementById(H.selectTypes[D]).options.length=0;for(var B in H[H.selectTypes[D]]){if(typeof H[H.selectTypes[D]][B]!="function"){var G=new Option(H[H.selectTypes[D]][B],H[H.selectTypes[D]][B],false,lsearch(H[H.selectTypes[D]][B],E[H.selectTypes[D]]));addOption(document.getElementById(H.selectTypes[D]),G)}}document.getElementById(H.selectTypes[D]).disabled=false;document.getElementById(H.selectTypes[D])}catch(F){}}}};this.fillSelects=A;this.onProductSelection=function(B){var E=[];for(var C=0;C','  ',"");UserLookup=function(A){UserLookup.superclass.constructor.call(this,{id:A.id||"user_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"getuser"},root:"users",totalProperty:"total",id:"login",fields:[{name:"login",mapping:"id"},{name:"name",mapping:"name"}]}),listeners:{valid:function(B){B.value=B.getRawValue()},beforequery:function(C){if(A.multistring){var B=C.query.match(/(^.*),(.*)/);if(B){C.combo.multivalue=B[1];C.query=B[2]}}},select:function(E,D,C){if(A.multistring){var B=E.multivalue||"";B=B?B+", "+D.get("login"):D.get("login");E.setValue(B)}}},queryParam:"search",loadingText:"Looking up users...",displayField:"login",valueField:"login",typeAhead:true,hideTrigger:true,minListWidth:300,forceSelection:false,emptyText:"Type a username...",pageSize:20,tpl:'
{name}
{login}
'});Ext.apply(this,A)};Ext.extend(UserLookup,Ext.form.ComboBox);TagLookup=function(A){TagLookup.superclass.constructor.call(this,{id:A.id||"tag_lookup",store:new Ext.data.JsonStore({url:"tr_quicksearch.cgi",baseParams:{action:"gettag"},root:"tags",totalProperty:"total",fields:[{name:"id",mapping:"tag_id"},{name:"name",mapping:"tag_name"}]}),queryParam:"search",loadingText:"Looking up tags...",displayField:"name",valueField:"id",typeAhead:false,hiddenName:"tag",hideTrigger:true,minListWidth:300,minChars:2,width:150,editable:true,forceSelection:false,emptyText:"Type a tagname...",listeners:{specialkey:function(B,C){if(C.getKey()==C.ENTER){Ext.getCmp("tag_add_btn").fireEvent("click")}}}});Ext.apply(this,A)};Ext.extend(TagLookup,Ext.form.ComboBox);BuildCombo=function(A){BuildCombo.superclass.constructor.call(this,{id:A.id||"build_combo",store:A.transform?false:new BuildStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up builds...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Builds..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(BuildCombo,Ext.form.ComboBox);CaseCategoryCombo=function(A){CaseCategoryCombo.superclass.constructor.call(this,{id:A.id||"case_category_combo",store:A.transform?false:new CaseCategoryStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up categories...",displayField:"name",valueField:"category_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseCategoryCombo,Ext.form.ComboBox);EnvironmentCombo=function(A){if(A.params){A.params.viewall=1}EnvironmentCombo.superclass.constructor.call(this,{id:A.id||"environment_combo",store:A.transform?false:new EnvironmentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up environments...",displayField:"name",valueField:"environment_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Environments..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(EnvironmentCombo,Ext.form.ComboBox);ProductCombo=function(A){ProductCombo.superclass.constructor.call(this,{id:A.id||"product_combo",store:A.transform?false:new ProductStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up products...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductCombo,Ext.form.ComboBox);ProductVersionCombo=function(A){ProductVersionCombo.superclass.constructor.call(this,{id:A.id||"product_version_combo",store:A.transform?false:new ProductVersionStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up versions...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ProductVersionCombo,Ext.form.ComboBox);CaseRunStatusCombo=function(A){CaseRunStatusCombo.superclass.constructor.call(this,{id:A.id||"case_run_status_combo",store:A.transform?false:new CaseRunStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseRunStatusCombo,Ext.form.ComboBox);CaseStatusCombo=function(A){CaseStatusCombo.superclass.constructor.call(this,{id:A.id||"case_status_combo",store:A.transform?false:new CaseStatusStore(A.mode=="local"?true:false),loadingText:"Looking up statuses...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(CaseStatusCombo,Ext.form.ComboBox);ComponentCombo=function(A){ComponentCombo.superclass.constructor.call(this,{id:A.id||"component_combo",store:A.transform?false:new ComponentStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up Components...",displayField:"name",valueField:"id",editable:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(ComponentCombo,Ext.form.ComboBox);MilestoneCombo=function(A){MilestoneCombo.superclass.constructor.call(this,{id:A.id||"milestone_combo",store:A.transform?false:new MilestoneStore(A.params,A.mode=="local"?true:false),loadingText:"Looking up milestones...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(MilestoneCombo,Ext.form.ComboBox);PlanTypesCombo=function(A){PlanTypesCombo.superclass.constructor.call(this,{id:A.id||"plan_type_combo",store:A.transform?false:new PlanTypesStore(A.mode=="local"?true:false),loadingText:"Looking up types...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PlanTypesCombo,Ext.form.ComboBox);PriorityCombo=function(A){PriorityCombo.superclass.constructor.call(this,{id:A.id||"priority_combo",store:A.transform?false:new PriorityStore(A.mode=="local"?true:false),loadingText:"Looking up priorities...",displayField:"name",valueField:"id",typeAhead:true,triggerAction:"all",minListWidth:100,forceSelection:true,transform:A.transform,emptyText:"Please select..."});Ext.apply(this,A);this.store.on("load",function(){if(A.value){this.setValue(A.value)}},this)};Ext.extend(PriorityCombo,Ext.form.ComboBox);RunProgress=function(A){RunProgress.superclass.constructor.call(this,A)};Ext.extend(RunProgress,Ext.ProgressBar,{onRender:function(C,A){Ext.ProgressBar.superclass.onRender.call(this,C,A);var B=new Ext.Template('
','
','
','
','
','
',"
 
","
",'
',"
 
","
","
","
");if(A){this.el=B.insertBefore(A,{cls:this.baseCls},true)}else{this.el=B.append(C,{cls:this.baseCls},true)}if(this.id){this.el.dom.id=this.id}this.progressBar=Ext.get(this.el.dom.firstChild);this.gbar=Ext.get(this.progressBar.dom.firstChild);this.rbar=Ext.get(this.gbar.dom.nextSibling);this.obar=Ext.get(this.rbar.dom.nextSibling);if(this.textEl){this.textEl=Ext.get(this.textEl);delete this.textTopEl}else{this.textTopEl=Ext.get(this.progressBar.dom.childNodes[3]);var D=Ext.get(this.progressBar.dom.childNodes[4]);this.textTopEl.setStyle("z-index",99).addClass("x-hidden");this.textEl=new Ext.CompositeElement([this.textTopEl.dom.firstChild,D.dom.firstChild]);this.textEl.setWidth(this.progressBar.offsetWidth)}if(this.gvalue||this.rvalue||this.ovalue){this.updateProgress(this.gvalue,this.rvalue,this.ovalue,this.text)}else{this.updateText(this.text)}this.setSize(this.width||"auto","auto");this.progressBar.setHeight(this.progressBar.offsetHeight)},updateProgress:function(C,B,D,G){this.gvalue=C||0;this.rvalue=B||0;this.ovalue=D||0;if(G){this.updateText(G)}var F=Math.floor(C*this.el.dom.firstChild.offsetWidth);var E=Math.floor(B*this.el.dom.firstChild.offsetWidth);var A=Math.floor(D*this.el.dom.firstChild.offsetWidth);this.gbar.setWidth(F);this.rbar.setWidth(E);this.obar.setWidth(A);return this},setSize:function(A,B){Ext.ProgressBar.superclass.setSize.call(this,A,B);if(this.textTopEl){this.textEl.setSize(this.el.dom.offsetWidth,this.el.dom.offsetHeight)}return this}});DocCompareToolbar=function(B,C){var A=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"getdocversions",object:B,object_id:C},root:"list",fields:[{name:"id",mapping:"id"},{name:"name",mapping:"name"}]});this.toolbar=new Ext.Toolbar({id:"doc_compare_tbar",items:[new Ext.Toolbar.Fill(),new Ext.form.ComboBox({id:"doc_view",store:A,displayField:"name",valueField:"id",width:50,triggerAction:"all"}),{xtype:"button",id:"doc_view_btn",text:"View Version",handler:function(){var D=Ext.getCmp("object_panel").add({title:"Version "+Ext.getCmp("doc_view").getValue(),closable:true,autoScroll:true});D.show();D.load({url:"tr_history.cgi",params:{action:"showdoc",object:B,object_id:C,version:Ext.getCmp("doc_view").getValue()},failure:testopiaError})}}]});return this.toolbar};HistoryGrid=function(A,B){this.store=new Ext.data.JsonStore({url:"tr_history.cgi",baseParams:{action:"show",object:A,object_id:B},root:"list",fields:[{name:"what",mapping:"what"},{name:"who",mapping:"who"},{name:"oldvalue",mapping:"oldvalue"},{name:"newvalue",mapping:"newvalue"},{name:"when",mapping:"changed"}]});this.columns=[{header:"What",width:150,dataIndex:"what",sortable:true},{header:"Who",width:180,sortable:true,dataIndex:"who"},{header:"When",width:150,sortable:true,dataIndex:"when"},{header:"Old",width:180,sortable:true,dataIndex:"oldvalue"},{id:"new",header:"New",width:180,sortable:true,dataIndex:"newvalue"}];HistoryGrid.superclass.constructor.call(this,{title:"Change History",id:"history-grid",layout:"fit",loadMask:{msg:"Loading History..."},autoExpandColumn:"new",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false})});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(HistoryGrid,Ext.grid.GridPanel,{onActivate:function(){if(!this.store.getCount()){this.store.load()}},onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"history-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())}});Ext.override(Ext.form.Field,{fireKey:function(A){if(((Ext.isIE&&A.type=="keydown")||A.type=="keypress")&&A.isSpecialKey()){this.fireEvent("specialkey",this,A)}else{this.fireEvent(A.type,this,A)}},initEvents:function(){this.el.on("focus",this.onFocus,this);this.el.on("blur",this.onBlur,this);this.el.on("keydown",this.fireKey,this);this.el.on("keypress",this.fireKey,this);this.el.on("keyup",this.fireKey,this);this.originalValue=this.getValue()}});var TestopiaPager=function(F,G){this.type=F;var H=E(G.baseParams);function I(){this.updateInfo()}function E(L){var K=new Object();for(var J in L){K[J]=L[J]}return K}function D(){this.cursor=0;this.afterTextEl.el.innerHTML=String.format(this.afterPageText,1);this.field.dom.value=1;this.updateInfo()}var A=new Ext.form.ComboBox({store:new Ext.data.SimpleStore({fields:["value","name"],id:0,data:[[25,25],[50,50],[100,100],[500,500]],autoLoad:true}),id:F+"_page_sizer",mode:"local",displayField:"name",valueField:"value",triggerAction:"all",editable:false,width:50});A.on("select",function(L,K,J){this.pageSize=K.get("value");Ext.state.Manager.set("TESTOPIA_DEFAULT_PAGE_SIZE",K.get("value"));G.baseParams.limit=K.get("value");G.load({params:{start:0},callback:I.createDelegate(this)})},this);this.sizer=A;var C=new Ext.Button({text:"View All",enableToggle:true});C.on("toggle",function(J,K){if(K){this.pageSize=0;G.load({params:{viewall:1},callback:D.createDelegate(this)})}else{this.pageSize=A.getValue();G.load({params:{start:0,limit:A.getValue()}})}},this);var B=new Ext.form.TextField({allowBlank:true,id:F+"_paging_filter",selectOnFocus:true});B.on("specialkey",function(N,O){var K=O.getKey();if(K==O.ENTER){var P={start:0,limit:A.getValue()};if(this.getValue().length===0){G.baseParams=E(H);G.load({params:P});Ext.getCmp(F+"_filtered_txt").hide();return }var P={start:0,limit:A.getValue()};var L=this.getValue();var J=L.match(/(^.*?):/);if(J){J=J[1];var M=Testopia.Util.trim(L.substr(L.indexOf(":")+1,L.length));if(J.match(/^start/i)){J="start_date"}if(J.match(/^stop/i)){J="stop_date"}if(J.match(/^manager/i)){J="manager"}switch(J){case"status":if(F=="case"){J="case_status"}else{if(F=="caserun"){J="case_run_status"}else{J="run_status";if(M.match(/running/i)){M=0}else{M=1}}}break;case"tester":J="default_tester";break;case"plan":J="plan_id";break;case"case":J="case_id";break;case"run":J="run_id";break;case"product_version":J="default_product_version";break}G.baseParams[J]=M;G.baseParams[J+"_type"]="substring"}else{if(F=="case"||F=="run"){G.baseParams.summary=this.getValue();G.baseParams.summary_type="allwordssubst"}else{if(F=="caserun"){G.baseParams.case_summary=this.getValue();G.baseParams.case_summary_type="allwordssubst"}else{G.baseParams.name=this.getValue();G.baseParams.name_type="allwordssubst"}}}G.load({params:P});Ext.getCmp(F+"_filtered_txt").show()}if((K==O.BACKSPACE||K==O.DELETE)&&this.getValue().length===0){G.baseParams=H;G.load({params:{start:0,limit:A.getValue()}});Ext.getCmp(F+"_filtered_txt").hide()}});A.on("render",function(){var J=new Ext.ToolTip({target:F+"_paging_filter",title:"Quick Search Filter",hideDelay:"500",html:"Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear"})});TestopiaPager.superclass.constructor.call(this,{id:F+"_pager",pageSize:Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25),displayInfo:true,displayMsg:"Displaying test "+F+"s {0} - {1} of {2}",emptyMsg:"No test "+F+"s were found",store:G,items:[new Ext.menu.TextItem("Filter: "),B,new Ext.Toolbar.Spacer("_"),new Ext.Toolbar.Separator(),new Ext.menu.TextItem("View "),new Ext.Toolbar.Spacer("_"),A,new Ext.Toolbar.Spacer("_"),C,new Ext.Toolbar.Spacer("_"),new ToolbarText({text:"(FILTERED)",hidden:true,id:F+"_filtered_txt",style:"font-weight:bold;color:red"})]});this.on("render",this.setPager,this);this.cursor=0};Ext.extend(TestopiaPager,Ext.PagingToolbar,{setPager:function(){Ext.getCmp(this.type+"_page_sizer").setValue(Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25))}});var ToolbarText=function(A){ToolbarText.superclass.constructor.call(this,{id:A.id,text:A.text,style:A.style,hidden:A.hidden})};Ext.extend(ToolbarText,Ext.menu.BaseItem,{hideOnClick:false,itemCls:"x-menu-text",onRender:function(){var A=document.createElement("span");A.className=this.itemCls;A.innerHTML=this.text;this.el=A;Ext.menu.TextItem.superclass.onRender.apply(this,arguments)}});DashboardPanel=function(A){DashboardPanel.superclass.constructor.call(this,{title:A.title||"Dashboard",layout:"fit",closable:A.closable||false,id:A.id||"dashboardpanel",tbar:[{xtype:"button",text:"Add Custom Panel",handler:function(B,C){Ext.Msg.prompt("Enter URL","",function(E,G){if(E=="ok"){var D=G+"&noheader=1";Testopia.Search.dashboard_urls.push(D);var F=new Ext.ux.Portlet({title:"Custom",closable:true,autoScroll:true,tools:PortalTools,url:D});Ext.getCmp("dashboard_leftcol").add(F);Ext.getCmp("dashboard_leftcol").doLayout();F.load({url:D,scripts:false})}})}},new Ext.Toolbar.Fill()],items:[{xtype:"portal",margins:"35 5 5 0",items:[{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.lc||"dashboard_leftcol",items:[{title:" ",hidden:true}]},{columnWidth:0.5,baseCls:"x-plain",bodyStyle:"padding:10px 10px 10px 10px",id:A.rc||"dashboard_rightcol",items:[{title:" ",hidden:true}]}]}]});this.on("activate",this.onActivate,this)};Ext.extend(DashboardPanel,Ext.Panel,{onActivate:function(A){A.doLayout()}});TestopiaUpdateMultiple=function(B,D,A){var C=new Ext.form.BasicForm("testopia_helper_frm",{});D.ctype="json";D.action="update";C.submit({url:"tr_list_"+B+"s.cgi",params:D,success:function(G,E){if(B=="caserun"){Ext.getCmp("run_progress").updateProgress(E.result.passed,E.result.failed,E.result.blocked,E.result.complete)}TestopiaUtil.notify.msg("Test "+B+"s updated","The selected {0}s were updated successfully",B);if(A.selectedRows){A.store.baseParams.addcases=A.selectedRows.join(",");Ext.getCmp(B+"_filtered_txt").show()}try{Ext.getCmp("case_details_panel").store.reload()}catch(F){}A.store.reload({callback:function(){if(A.selectedRows){var K=A.getSelectionModel();var J=[];for(var I=0;I=0){J.push(H)}}K.selectRows(J);if(K.getCount()<1){Ext.getCmp("case_details_panel").disable()}}}})},failure:function(F,E){testopiaError(F,E);A.store.reload({callback:function(){if(A.selectedRows){A.getSelectionModel().selectRows(A.selectedRows)}}})}})};TestopiaComboRenderer=function(B,F,E,A,C,D){f=this.getColumnModel().getCellEditor(C,A).field;record=f.store.getById(B);if(record){return record.data[f.displayField]}else{return B}};testopiaError=function(C,A){C.el.unmask();var B;if(A.response.status&&A.response.status!=200){B={title:"System Error!",msg:A.response.responseText,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}else{B={title:"An Error Has Occurred",msg:A.result.message,buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR,minWidth:400}}Ext.Msg.show(B)};testopiaLoadError=function(){Ext.Msg.show({title:"An Error Has Occurred",msg:"There was an error loading the data",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR})};getSelectedObjects=function(C,F){var E=C.getSelectionModel().getSelections();var A=[];var D;for(var B=0;B")}else{if(B=="custom"){D=E;E={report:true};A=1}else{if(B=="caserun"){E.current_tab="case_run"}else{E.current_tab=B}if(E.report){D="tr_"+B+"_reports.cgi?";A=1}else{D="tr_list_"+B+"s.cgi?";A=0}D=D+jsonToSearch(E,"",["ctype"])}}var C=new Ext.form.BasicForm("testopia_helper_frm",{});Ext.Msg.prompt("Save As","",function(F,G){if(F=="ok"){C.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:G,query_part:D,type:A},success:function(){if(Ext.getCmp("searches_grid")){Ext.getCmp("searches_grid").store.load()}if(Ext.getCmp("reports_grid")){Ext.getCmp("reports_grid").store.load()}if(Ext.getCmp("dashboard_grid")){Ext.getCmp("dashboard_grid").store.load()}TestopiaUtil.notify.msg("Saved","Your search or report was saved.")},failure:testopiaError})}})};linkPopup=function(E){if(E.current_tab=="case_run"){E.current_tab="caserun"}var B;if(E.report==1){B="tr_"+E.current_tab+"_reports.cgi"}else{B="tr_list_"+E.current_tab+"s.cgi"}var A=window.location;var C=A.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);C=C[1];var D=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:A.protocol+"//"+A.host+C+"/"+B+"?"+jsonToSearch(E,"",["ctype"]),width:287})]});D.show()};searchToJson=function(A){A=A.replace(/.*\//,"");var H={};var G=A.split("?",2);var D=G[0];var C=G[1]?G[1]:D;var E=C.split("&");for(var B=0;B','
','

',C,"

",D,"
",'
',""].join("")}return{msg:function(F,E){if(!B){B=Ext.DomHelper.insertFirst(document.getElementById("bugzilla-body"),{id:"msg-div"},true)}B.alignTo(document,"t-t");var D=String.format.apply(String,Array.prototype.slice.call(arguments,1));var C=Ext.DomHelper.append(B,{html:A(F,D)},true);C.slideIn("t").pause(1).ghost("t",{remove:true})},init:function(){return }}}();Testopia.Util.trim=function(A){A=A.replace(/^\s+/g,"");A=A.replace(/\s+$/g,"");return A};Testopia.Util.PlanSelector=function(B,A){var E=A.action.match("case")?false:true;var D=new PlanGrid({product_id:B},{id:"plan_selector_grid",height:300,single:E});var C=new ProductCombo({mode:"local",value:B});C.on("select",function(H,G,F){D.store.baseParams={ctype:"json",product_id:G.get("id")};D.store.load()});Testopia.Util.PlanSelector.superclass.constructor.call(this,{items:[D],buttons:[{text:"Use Selected",handler:function(){var F=A.action+"?plan_id="+getSelectedObjects(D,"plan_id");if(A.bug_id){F=F+"&bug="+A.bug_id}window.location=F}}]});D.on("render",function(){var F=D.getTopToolbar().items.items;for(var G=0;G'+D+""}this.object=A;this.store=new Ext.data.JsonStore({url:"tr_attachment.cgi",root:"attachment",baseParams:{ctype:"json",action:"list",object:this.object.type,object_id:this.object.id},id:"attach_id",fields:[{name:"id",mapping:"attachment_id"},{name:"submitter",mapping:"submitter"},{name:"caserun_id",mapping:"caserun_id"},{name:"name",mapping:"filename"},{name:"timestamp",mapping:"creation_ts"},{name:"mimetype",mapping:"mime_type"},{name:"description",mapping:"description"},{name:"isviewable",mapping:"isviewable"},{name:"canedit",mapping:"canedit"},{name:"candelete",mapping:"candelete"},{name:"size",mapping:"datasize"}]});var C=this.store;this.columns=[{id:"attach_id",header:"ID",width:20,sortable:true,dataIndex:"id",renderer:B},{header:"Created",width:50,sortable:true,dataIndex:"timestamp",renderer:function(D,F,E){if(E.get("caserun_id")&&Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")==E.get("caserun_id")){return"* "+D+""}else{return D}}},{header:"Name",width:50,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"name"},{header:"Submitted by",width:50,sortable:true,dataIndex:"submitter"},{header:"Type",width:30,editor:new Ext.grid.GridEditor(new Ext.form.TextField({})),sortable:true,dataIndex:"mimetype"},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"},{header:"Size",width:50,sortable:true,dataIndex:"size",renderer:function(D){if(D){return D+" Bytes"}}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});AttachGrid.superclass.constructor.call(this,{title:"Attachments",id:"attachments_panel",loadMask:{msg:"Loading attachments..."},autoExpandColumn:"Name",autoScroll:true,enableColumnHide:true,tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_attachment_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Attachments",handler:function(){editFirstSelection(Ext.getCmp("attachments_panel"))}},{xtype:"button",id:"add_attachment_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Attach a new file",handler:this.newAttachment.createDelegate(this)},{xtype:"button",id:"delete_attachment_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Remove selected attachments",handler:this.deleteAttachment.createDelegate(this)}],sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(F,D,E){if(E.get("candelete")){Ext.getCmp("delete_attachment_btn").enable()}if(E.get("canedit")){Ext.getCmp("edit_attachment_btn").enable()}},rowdeselect:function(F,D,E){if(F.getCount()<1){Ext.getCmp("delete_attachment_btn").disable();Ext.getCmp("edit_attachment_btn").disable()}}}}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(AttachGrid,Ext.grid.EditorGridPanel,{onContextClick:function(C,B,D){var E=this.selectionModel;var A=this.object;if(!this.menu){this.menu=new Ext.menu.Menu({id:"AttachGrid-ctx-menu",items:[{text:"Delete Selected Attachments",id:"attach_delete_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,handler:this.deleteAttachment.createDelegate(this)},{text:"Reload List",handler:function(){C.store.reload()}}]})}D.stopEvent();if(C.getSelectionModel().getCount()<1){C.getSelectionModel().selectRow(B)}if(C.getSelectionModel().getSelected().get("candelete")){Ext.getCmp("attach_delete_mnu").enable()}else{Ext.getCmp("attach_delete_mnu").enable()}this.menu.showAt(D.getXY())},onGridEdit:function(B){var A={action:"edit",ctype:"json",attach_id:this.store.getAt(B.row).get("id")};var C=this.store;switch(B.field){case"name":A.filename=B.value;break;case"mime_type":A.mime_type=B.value;break;case"description":A.description=B.value;break}this.form.submit({url:"tr_attachment.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},newAttachment:function(){var A=new NewAttachmentPopup(this.object);A.window.show()},deleteAttachment:function(){object=this.object;Ext.Msg.show({title:"Confirm Delete?",msg:ATTACHMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_attachment.cgi",params:{attach_ids:getSelectedObjects(Ext.getCmp("attachments_panel"),"id"),action:"remove",ctype:"json",object:object.type,object_id:object.id},success:function(){Ext.getCmp("attachments_panel").store.load()},failure:testopiaError})}},animEl:"delete_attachment_btn",icon:Ext.MessageBox.QUESTION})},onActivate:function(A){if(this.object.type=="caserun"){this.store.baseParams={ctype:"json",action:"list",object:"caserun",object_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")};this.store.load()}if(!this.store.getCount()){this.store.load()}}});AttachForm=function(){var A=1;AttachForm.superclass.constructor.call(this,{title:"Attachments",id:"attachments_form",autoScroll:true,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",bodyStyle:"padding: 5px 5px 10px 10px",id:"attach_file_col",items:[{xtype:"field",fieldLabel:"Attachment",inputType:"file",name:"file1",width:300}]},{columnWidth:0.5,id:"attach_desc_col",bodyStyle:"padding: 5px 5px 10px 10px",layout:"form",items:[{xtype:"textfield",fieldLabel:"Description",name:"file_desc1",width:300}]}]}],buttons:[{text:"Attach Another",handler:function(){A++;if(A>4){Ext.Msg.show({msg:"You may only attach 4 files at a time",title:"Limit Exceeded",buttons:Ext.Msg.OK,icon:Ext.MessageBox.WARNING});return }Ext.getCmp("attach_file_col").add(new Ext.form.Field({fieldLabel:"Attachment",inputType:"file",name:"file"+A,width:300}));Ext.getCmp("attach_desc_col").add(new Ext.form.Field({fieldLabel:"Description",name:"file_desc"+A,width:300}));Ext.getCmp("attachments_form").doLayout()}}]});this.on("activate",this.onActivate,this)};Ext.extend(AttachForm,Ext.Panel,{onActivate:function(){Ext.getCmp("attachments_form").doLayout()}});NewAttachmentPopup=function(A){if(!this.window){var B=new Ext.Window({id:"new_attachment_win",title:"Attach a file",closable:true,width:400,height:180,plain:true,shadow:false,closable:false,layout:"fit",items:[{xtype:"form",id:"new_attach_frm",fileUpload:true,bodyStyle:"padding: 10px",items:[{xtype:"textfield",id:"attach_desc",fieldLabel:"Description",name:"description",allowBlank:false},{xtype:"field",id:"attach_file",inputType:"file",fieldLabel:"File",name:"data",allowBlank:false}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("new_attach_frm").getForm().submit({url:"tr_attachment.cgi",params:{action:"add",object:A.type,object_id:A.id,ctype:"json"},success:function(){Ext.getCmp("attachments_panel").store.load();Ext.getCmp("new_attachment_win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("new_attachment_win").close()}}]});this.window=B}return this};Testopia.TestPlan={};Testopia.TestPlan.ImportWin=function(A){var B=new Ext.Window({id:"import-win",closable:true,width:450,height:150,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",height:250,url:"tr_importer.cgi",id:"importform",baseParams:{action:"upload",ctype:"json",plan_id:A},fileUpload:true,items:[{height:50,style:"padding: 5px",border:false,html:'Accepts CSV and XML files under 1 MB in size.
See import_example.csv and testopia.dtd for proper format.'},{xtype:"field",fieldLabel:"Upload File",labelStyle:"padding: 5px",inputType:"file",name:"data",width:300}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("importform").getForm().submit({success:function(){Ext.getCmp("object_panel").activate("plan_case_grid");Ext.getCmp("plan_case_grid").store.load();Ext.getCmp("import-win").close()},failure:testopiaError})}}]}]});B.show(this)};PlanGrid=function(E,A){E.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);E.current_tab="plan";this.params=E;var B=new TestopiaUtil();this.t=B;var D=new ProductVersionCombo({id:"plan_grid_version_chooser",hiddenName:"prod_version",mode:"remote",params:{product_id:E.product_id}});this.store=new TestPlanStore(E);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"plan_id",sortable:true,renderer:B.planLink,hideable:false},{header:"Name",width:220,dataIndex:"name",id:"plan_name",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author"},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Product",width:180,sortable:true,dataIndex:"product",hidden:true},{header:"Product Version",width:60,sortable:true,dataIndex:"default_product_version",editor:new Ext.grid.GridEditor(D,{listeners:{startedit:function(){var F=Ext.getCmp(A.id||"plan_grid").getSelectionModel().getSelected().get("product_id");if(D.store.baseParams.product_id!=F){D.store.baseParams.product_id=F;D.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Type",width:60,sortable:true,dataIndex:"plan_type",editor:new Ext.grid.GridEditor(new PlanTypesCombo({id:"plan_grid_ types_chooser",hiddenName:"type",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Cases",width:20,sortable:false,dataIndex:"case_count"},{header:"Runs",width:20,sortable:false,dataIndex:"run_count"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("plan",this.store);PlanGrid.superclass.constructor.call(this,{title:"Test Plans",id:A.id||"plan_grid",layout:"fit",region:"center",stripeRows:true,loadMask:{msg:"Loading Test Plans..."},autoExpandColumn:"plan_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:A.single||false,listeners:{rowselect:function(H,F,G){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").enable()}if(Ext.getCmp("plan_add_case_mnu")){Ext.getCmp("plan_add_case_mnu").enable()}if(Ext.getCmp("plan_grid_edit_mnu")){Ext.getCmp("plan_grid_edit_mnu").enable()}Ext.getCmp("new_run_button").enable();Ext.getCmp("new_case_button").enable();Ext.getCmp("edit_plan_list_btn").enable();if(H.getCount()>1){if(Ext.getCmp("plan_add_run_mnu")){Ext.getCmp("plan_add_run_mnu").disable()}Ext.getCmp("new_run_button").disable()}},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("new_run_button").disable();Ext.getCmp("new_case_button").disable();Ext.getCmp("edit_plan_list_btn").disable()}}}}),enableColumnHide:true,tbar:[{xtype:"button",text:"New Run",id:"new_run_button",disabled:true,handler:this.newRun.createDelegate(this)},{xtype:"button",text:"New Case",id:"new_case_button",disabled:true,handler:this.newCase.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_plan_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(F,G){saveSearch("plan",Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"link_plan_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(F,G){linkPopup(Ext.getCmp(A.id||"plan_grid").store.baseParams)}},{xtype:"button",id:"edit_plan_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Plan",handler:function(){editFirstSelection(Ext.getCmp(A.id||"plan_grid"))}},{xtype:"button",id:"new_plan_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Plan",handler:function(){B.newPlanPopup(E.product_id)}}],viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(PlanGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"plan-ctx-menu",items:[{text:"Create a New Test Plan",id:"plan_menu_new_plan",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:this.newPlan.createDelegate(this)},{text:"Add a New Test Run to Selected Plan",id:"plan_add_run_mnu",handler:this.newRun.createDelegate(this)},{text:"Add a New Test Case to Selected Plans",id:"plan_add_case_mnu",handler:this.newCase.createDelegate(this)},{text:"Edit",id:"plan_grid_edit_mnu",menu:{items:[{text:"Type",handler:function(){var D=new Ext.Window({title:"Change Plan Type",id:"plan_type_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new PlanTypesCombo({id:"plan_type_win_types_combo",fieldLabel:"Plan Type"})]})],buttons:[{text:"Update Type",handler:function(){var E={plan_type:Ext.getCmp("plan_type_combo").getValue(),ids:getSelectedObjects(B,"plan_id")};TestopiaUpdateMultiple("plan",E,B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("plan",B)}}]}},{text:"Reports",menu:{items:[{text:"New Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"plan_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"}]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&plan_ids="+getSelectedObjects(B,"plan_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&plan_ids="+getSelectedObjects(B,"plan_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&plan_ids="+getSelectedObjects(B,"plan_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}},{text:"Missing Cases Report",handler:function(){window.open("tr_list_cases.cgi?report_type=missing&plan_ids="+getSelectedObjects(B,"plan_id"))}}]}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Plan(s) in a New Tab",handler:function(){var E=getSelectedObjects(B,"plan_id").split(",");var D;for(D=0;DProduct Version",mode:"local",params:{product_id:C}});var B=new ProductCombo({id:"new_plan_form_product_chooser",hiddenName:"product_id",fieldLabel:"Product",mode:"local",value:C});B.on("select",function(F,E,D){A.reset();A.store.baseParams.product_id=E.get("id");A.store.load();A.enable()});NewPlanForm.superclass.constructor.call(this,{url:"tr_new_plan.cgi",id:"newplanform",baseParams:{action:"add"},fileUpload:true,labelAlign:"top",frame:true,title:"New Plan",bodyStyle:"padding:5px 5px 0",width:800,height:500,items:[{layout:"column",items:[{columnWidth:0.5,layout:"form",items:[{xtype:"textfield",fieldLabel:"Plan Name",name:"plan_name",anchor:"95%",allowBlank:false},new PlanTypesCombo({id:"new_plan_form_types_chooser",mode:"local",hiddenName:"type",fieldLabel:"Plan Type"})]},{columnWidth:0.5,layout:"form",items:[B,A]}]},{xtype:"tabpanel",height:280,activeItem:0,items:[{layout:"fit",title:"Plan Document",items:[{id:"plan_doc",xtype:"htmleditor",name:"plandoc"}]},new AttachForm()]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newplanform").getForm().isValid()){return }Ext.getCmp("newplanform").getForm().submit({success:function(E,F){if(F.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Plan Created",msg:"Plan "+F.result.plan+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(G){if(G=="yes"){window.location="tr_show_plan.cgi?plan_id="+F.result.plan}}});try{Ext.getCmp("newplan-win").close()}catch(D){}},failure:testopiaError})}},{text:"Cancel",handler:function(){if(Ext.getCmp("newplan-win")){Ext.getCmp("newplan-win").close()}else{window.location="tr_show_product.cgi"}}}]})};Ext.extend(NewPlanForm,Ext.form.FormPanel);Testopia.TestPlan.ClonePanel=function(E){var B=new ProductCombo({id:"plan_clone_product_chooser",hiddenName:"product_id",fieldLabel:"Copy To Product",mode:"local",width:550,value:E.product_id});var C=new ProductVersionCombo({id:"plan_clone_version_chooser",hiddenName:"prod_version",fieldLabel:"Product Version",params:{product_id:E.product_id},allowBlank:false});var F=new BuildCombo({fieldLabel:"Select a Build",id:"plan_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:E.product_id,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"plan_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:E.product_id}});B.on("select",function(I,H,G){C.reset();C.store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_environment_chooser").store.baseParams.product_id=H.id;Ext.getCmp("plan_clone_build_chooser").store.load();Ext.getCmp("plan_clone_environment_chooser").store.load();if(H.id==E.product_id){Ext.getCmp("copy_categories").disable()}else{Ext.getCmp("copy_categories").enable()}C.store.load();C.enable()});function D(){var G=this.getForm();var H=G.getValues();if(G.isValid()){G.submit({success:function(J,I){Ext.Msg.show({title:"Plan Copied",msg:"Plan "+I.result.plan_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(K){if(K=="yes"){window.location="tr_show_plan.cgi?plan_id="+I.result.plan_id}}})},failure:testopiaError})}}Testopia.TestPlan.ClonePanel.superclass.constructor.call(this,{id:"plan_clone_panel",url:"tr_process_plan.cgi",baseParams:{action:"clone"},bodyStyle:"padding: 10px",border:false,autoScroll:true,width:600,items:[{layout:"table",border:false,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"plan_clone_name",xtype:"textfield",fieldLabel:"New Plan Name",name:"plan_name",allowBlank:false,width:550},B,C]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_attachments",checked:false,boxLabel:"Copy Plan Attachments",hideLabel:true},{xtype:"checkbox",name:"copy_doc",checked:true,boxLabel:"Copy Plan Document",hideLabel:true},{xtype:"hidden",name:"plan_id",value:E.plan_id}]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Plan Tags",hideLabel:true},{xtype:"checkbox",name:"copy_perms",checked:true,boxLabel:"Copy Plan Permissions",hideLabel:true}]},{layout:"form",border:false,colspan:2,items:[{xtype:"checkbox",name:"keep_plan_author",checked:false,boxLabel:"Maintain original author (unchecking will make me the author of the new plan)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"copy_cases",title:"Copy Test Cases",collapsed:true,items:[{xtype:"checkbox",id:"case_copy_plan_ids",name:"make_copy",boxLabel:"Create a copy (Unchecking will create a link to selected plans)",hideLabel:true,listeners:{check:function(H,G){if(G===true){Ext.getCmp("copy_cases_keep_author").enable();Ext.getCmp("copy_cases_keep_tester").enable();Ext.getCmp("copy_run_cases_cbox").disable()}else{Ext.getCmp("copy_cases_keep_author").disable();Ext.getCmp("copy_cases_keep_tester").disable();Ext.getCmp("copy_run_cases_cbox").enable()}}}},{xtype:"checkbox",name:"keep_case_authors",id:"copy_cases_keep_author",checked:false,disabled:true,boxLabel:"Maintain original authors (unchecking will make me the author of the copied cases)",hideLabel:true},{xtype:"checkbox",id:"copy_cases_keep_tester",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",name:"copy_categories",id:"copy_categories",checked:false,disabled:true,boxLabel:"Copy Categories to new product (unchecking will place copied cases in the default category for the selected product)",hideLabel:true}]},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_runs",id:"copy_runs",title:"Copy Test Runs",collapsed:true,items:[{xtype:"checkbox",name:"keep_run_managers",checked:false,boxLabel:"Maintain managers (unchecking will make me the manager of the new runs)",hideLabel:true},{xtype:"checkbox",name:"copy_run_tags",checked:true,boxLabel:"Copy tags from the old run to the new run",hideLabel:true},{xtype:"checkbox",name:"copy_run_cases",id:"copy_run_cases_cbox",checked:true,boxLabel:"Link cases in copied run to original test cases (unchecking will produce an empty test run)",hideLabel:true},F,A]}]}]}],buttons:[{text:"Submit",handler:D.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("plan-clone-win").close()}}]})};Ext.extend(Testopia.TestPlan.ClonePanel,Ext.form.FormPanel);PlanClonePopup=function(B){var A=new Ext.Window({id:"plan-clone-win",closable:true,width:750,title:"Create a Copy of Plan "+B.plan_id,height:500,plain:true,shadow:false,closable:true,layout:"fit",items:[new Testopia.TestPlan.ClonePanel(B)]});A.show()};CasePanel=function(D,A){var B=new CaseGrid(D,A);var C=new CaseFilter();this.cgrid=B;this.store=B.store;this.params=D;CasePanel.superclass.constructor.call(this,{title:"Test Cases",layout:"border",id:"case-panel",items:[C,B]});this.on("activate",this.onActivate,this)};Ext.extend(CasePanel,Ext.Panel,{onActivate:function(A){if(!this.store.getCount()){this.store.load({params:this.params})}}});CaseFilter=function(){this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseFilter.superclass.constructor.call(this,{title:"Search for Test Cases",region:"north",layout:"fit",frame:true,collapsible:true,height:120,items:[{buttons:[{text:"Search",handler:function(){Ext.getCmp("case_search").getForm().submit()}}]}]})};Ext.extend(CaseFilter,Ext.Panel);CaseGrid=function(D,A){D.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();D.current_tab="case";this.params=D;categoryCombo=new CaseCategoryCombo({id:"case_grid_cateogy_chooser",hiddenName:"category",mode:"remote",params:{}});this.store=new Ext.data.GroupingStore({url:"tr_list_cases.cgi",baseParams:D,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"case_id",fields:[{name:"case_id",mapping:"case_id"},{name:"sortkey",mapping:"sortkey"},{name:"plan_id",mapping:"plan_id"},{name:"alias",mapping:"alias"},{name:"summary",mapping:"summary"},{name:"author",mapping:"author_name"},{name:"tester",mapping:"default_tester"},{name:"creation_date",mapping:"creation_date"},{name:"category",mapping:"category_name"},{name:"priority",mapping:"priority"},{name:"status",mapping:"status"},{name:"run_count",mapping:"run_count"},{name:"requirement",mapping:"requirement"},{name:"product_id",mapping:"product_id"},{name:"component",mapping:"component"},{name:"modified",mapping:"modified"},{name:"isautomated",mapping:"isautomated"},{name:"plan_name",mapping:"plan_name"}]}),remoteSort:true,sortInfo:{field:"case_id",direction:"ASC"},groupField:D.plan_id?"":"plan_id"});var C=this.store;C.paramNames.sort="order";C.on("beforeload",function(E,F){E.baseParams.ctype="json"});this.columns=[{header:"ID",width:50,dataIndex:"case_id",sortable:true,groupRenderer:function(E){return E},renderer:B.caseLink,hideable:false},{header:"Sort Key",width:50,sortable:true,dataIndex:"sortkey",editor:new Ext.grid.GridEditor(new Ext.form.NumberField({allowBlank:true,allowDecimals:false,allowNegative:false})),id:"sortkey"},{header:"Summary",width:220,dataIndex:"summary",id:"case_summary",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}))},{header:"Author",width:150,sortable:true,dataIndex:"author",hidden:true},{header:"Default Tester",width:150,sortable:true,dataIndex:"tester",editor:new Ext.grid.GridEditor(new UserLookup({hiddenName:"tester"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Created",width:110,sortable:true,dataIndex:"creation_date",hidden:true},{header:"Last Modified",width:110,sortable:true,dataIndex:"modified",hidden:true},{header:"Priority",width:100,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({hiddenName:"priority",mode:"remote"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(categoryCombo,{listeners:{startedit:function(){var E=Ext.getCmp(A.id||"case_grid").getSelectionModel().getSelected().get("product_id");if(categoryCombo.store.baseParams.product_id!=E){categoryCombo.store.baseParams.product_id=E;categoryCombo.store.load()}}}}),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Component",width:110,sortable:true,dataIndex:"component"},{header:"Status",width:100,sortable:true,dataIndex:"status",editor:new Ext.grid.GridEditor(new CaseStatusCombo("status")),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:40,sortable:true,dataIndex:"requirement",hidden:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({name:"requirement"}))},{header:"Plan",width:40,sortable:true,dataIndex:"plan_id",hidden:true,renderer:B.plan_link,groupRenderer:function(E,F,G){return E+': "'+G.get("plan_name")+'"'}},{header:"Run Count",width:40,sortable:false,dataIndex:"run_count",hidden:true}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'});this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("case",this.store);CaseGrid.superclass.constructor.call(this,{title:"Test Cases",id:A.id||"case_grid",loadMask:{msg:"Loading Test Cases..."},layout:"fit",stripeRows:true,region:"center",autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(G,E,F){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").enable();Ext.getCmp("edit_case_list_btn").enable()}},rowdeselect:function(G,E,F){if(G.getCount()<1){if(Ext.getCmp("delete_case_list_btn")){Ext.getCmp("delete_case_list_btn").disable();Ext.getCmp("edit_case_list_btn").disable()}}}}}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_case_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("case",Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"case_grid").store.baseParams)}},{xtype:"button",id:"edit_case_list_btn",icon:"testopia/img/edit.png",disabled:true,iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp(A.id||"case_grid"))}},{xtype:"button",id:"add_case_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Case",handler:function(){try{if(plan){B.newCaseForm(plan.plan_id,plan.product_id)}}catch(E){window.location="tr_new_case.cgi"}}},{xtype:"button",template:button_16x_tmpl,id:"delete_case_list_btn",disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete Selected Test Cases",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,A);this.on("activate",this.onActivate,this);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,E){B.selindex=A;if(!this.menu){var D;try{D=plan?false:true}catch(C){D=true}this.menu=new Ext.menu.Menu({id:"case_list_ctx_menu",items:[{text:"Modify Selected Test Cases",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Requirements",handler:function(){Ext.Msg.prompt("Edit Requirements","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{requirement:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Category",disabled:D,handler:function(){var F=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:plan.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Status",handler:function(){var F=new Ext.Window({title:"Edit Status",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseStatusCombo({fieldLabel:"Status"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{status:Ext.getCmp("case_status_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Priority",handler:function(){var F=new Ext.Window({title:"Edit Priority",id:"priority-win",layout:"form",plain:true,shadow:false,width:300,height:150,labelWidth:30,items:[new PriorityCombo({fieldLabel:"Priority"})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{priority:Ext.getCmp("priority_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Close",handler:function(){F.close()}}]});F.show(this)}},{text:"Tester",handler:function(){var F=new Ext.Window({title:"Change Default Tester",id:"def_tester_win",layout:"fit",plain:true,shadow:false,split:true,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"tester_update",fieldLabel:"Default Tester"})]})],buttons:[{text:"Update Tester",handler:function(){TestopiaUpdateMultiple("case",{tester:Ext.getCmp("tester_update").getValue(),ids:getSelectedObjects(B,"case_id")},B);F.close()}},{text:"Cancel",handler:function(){F.close()}}]});F.show()}},{text:"Automation",handler:function(){var F=new Ext.form.Checkbox({checked:false,name:"isautomated",fieldLabel:"Enable Automation"});var G=new Ext.form.TextField({xtype:"textfield",disabled:true,name:"script",fieldLabel:"Script "});var I=new Ext.form.TextField({xtype:"textfield",name:"arguments",disabled:true,fieldLabel:"Arguments "});F.on("check",function(){if(G.disabled){G.enable();I.enable()}else{G.disable();I.disable()}},F);var H=new Ext.Window({title:"Edit Automation Settings",id:"auto-win",layout:"form",plain:true,shadow:false,width:350,height:250,items:[{id:"automation_form",bodyStyle:"padding: 5px",xtype:"form",items:[F,I,G]}],buttons:[{text:"Submit",handler:function(){params=Ext.getCmp("automation_form").getForm().getValues();params.ids=getSelectedObjects(B,"case_id");TestopiaUpdateMultiple("case",params,B);H.close()}},{text:"Close",handler:function(){H.close()}}]});H.show(this)}}]}},{text:"Delete Selected Test Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(F,G){if(F=="ok"){TestopiaUpdateMultiple("case",{addruns:G,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var F=B.getSelectionModel().getSelected();caseClonePopup(F.get("product_id"),getSelectedObjects(B,"case_id"))}},{text:"Unlink from Plan",disabled:D,handler:function(){Ext.Msg.show({title:"Unlink Selected Test Cases",msg:"You are about to unlink the selected test cases from this plan. If a test case is not linked to any other plans, it will be deleted. Do you want to continue?",buttons:Ext.Msg.YESNO,icon:Ext.Msg.WARNING,fn:function(G){if(G=="yes"){var F=new Ext.form.BasicForm("testopia_helper_frm");F.submit({url:"tr_list_cases.cgi",params:{case_ids:getSelectedObjects(B,"case_id"),action:"unlink",plan_id:plan.plan_id},success:function(H){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});B.store.reload()},failure:function(I,H){testopiaError(I,H);B.store.reload()}})}}})}},{text:"Add or Remove Tags from Selected Cases...",handler:function(){TagsUpdate("case",B)}},{text:"Add or Remove Bugs from Selected Cases...",handler:function(){BugsUpdate(B)}},{text:"Add or Remove Components from Selected Cases...",handler:function(){var F=new Ext.Window({title:"Add or Remove Components",id:"component_update_win",layout:"fit",split:true,plain:true,shadow:false,width:550,height:85,items:[new CaseComponentsGrid(B)]});F.show()}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case(s) in a New Tab",handler:function(){var F=getSelectedObjects(B,"case_id").split(",");var G;for(G=0;GSummary",name:"summary",allowBlank:false,width:800},{xtype:"hidden",name:"components",id:"compfield"},{xtype:"hidden",name:"plan_id",id:"planfield",value:C}]},{layout:"form",items:[new UserLookup({id:"default_tester",hiddenName:"tester",fieldLabel:"Default Tester"}),{xtype:"textfield",fieldLabel:"Alias",id:"case_alias",name:"alias"},new PriorityCombo({fieldLabel:'Priority  ',hiddenName:"priority",mode:"local",allowBlank:false}),new CaseCategoryCombo({fieldLabel:"Category",hiddenName:"category",mode:"local",allowBlank:false,params:{product_id:B}}),{xtype:"textfield",fieldLabel:"Estimated Time (HH:MM:SS)",id:"estimated_time",name:"estimated_time"},{xtype:"textfield",fieldLabel:"Bugs",id:"ncf-bugs",name:"bugs"},{xtype:"textfield",fieldLabel:"Blocks",id:"ncf-blocks",name:"tcblocks"}]},{layout:"form",items:[new CaseStatusCombo({fieldLabel:"Status",hiddenName:"status",mode:"local",value:DEFAULT_CASE_STATUS,allowBlank:false,id:"ncf-casestatus"}),{xtype:"textfield",fieldLabel:"Add Tags",id:"ncf-addtags",name:"addtags"},{xtype:"textfield",fieldLabel:"Requirements",id:"ncf-reqs",name:"requirement"},{xtype:"checkbox",fieldLabel:"Automated",id:"ncf-automated",name:"isautomated",value:"1"},{xtype:"textfield",fieldLabel:"Scripts",id:"ncf-scripts",name:"script"},{xtype:"textfield",fieldLabel:"Arguments",id:"ncf-arguments",name:"arguments"},{xtype:"textfield",fieldLabel:"Add to Run",id:"ncf-addtorun",name:"addruns",value:A},{xtype:"textfield",fieldLabel:"Depends On",id:"ncf-dependson",name:"tcdependson"}]}]},{xtype:"tabpanel",id:"ncf_tabs",height:356,activeItem:1,items:[{layout:"column",title:"Setup Procedures",items:[{columnWidth:0.5,items:[{title:"Setup",layout:"fit",items:[{id:"ncf-setup_doc",name:"tcsetup",xtype:"htmleditor",scrollable:true}]}]},{columnWidth:0.5,items:[{title:"Break Down",layout:"fit",items:[{id:"ncf-breakdown_doc",name:"tcbreakdown",xtype:"htmleditor",scrollable:true}]}]}]},{layout:"column",title:"Actions",items:[{columnWidth:0.5,items:[{title:"Action",layout:"fit",items:[{id:"ncf-action",name:"tcaction",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_action"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]},{columnWidth:0.5,items:[{title:"Expected Results",layout:"fit",items:[{id:"ncf-effect",name:"tceffect",xtype:"htmleditor",scrollable:true,listeners:{initialize:function(E){if(!E.getValue()){var D=new Ext.data.Connection();D.request({url:"tr_quicksearch.cgi",params:{action:"get_effect"},success:function(F){E.setValue(F.responseText)},failure:testopiaError})}}}}]}]}]},new AttachForm(),{title:"Components",id:"component_picker",height:250,layout:"fit",xtype:"grid",store:new ComponentStore({product_id:B},true),columns:[{sortable:true,dataIndex:"name",width:500}],sm:new Ext.grid.RowSelectionModel({singleSelect:false}),tbar:[new Ext.menu.TextItem("Product"),new Ext.Toolbar.Spacer(),new ProductCombo({mode:"local",value:B,id:"comp_product_combo"})]}]}],buttons:[{text:"Submit",handler:function(){if(!Ext.getCmp("newcaseform").getForm().isValid()){return }Ext.getCmp("newcaseform").getForm().submit({method:"POST",success:function(D,E){if(E.result.err){alert("One or more attachments were either too large or were empty. These have been ignored.")}Ext.Msg.show({title:"Test Case Created",msg:"Test case "+E.result.tc+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_case.cgi?case_id="+E.result.tc}}});if(Ext.getCmp("plan_case_grid")){Ext.getCmp("plan_case_grid").store.reload()}else{if(Ext.getCmp("newrun_casegrid")){Ext.getCmp("newrun_casegrid").store.reload()}else{if(Ext.getCmp("caserun_grid")){Ext.getCmp("caserun_grid").store.reload()}else{if(Ext.getCmp("product_case_grid")){Ext.getCmp("product_case_grid").store.reload()}}}}},failure:testopiaError})}},{text:"Cancel",id:"ncf_cancel_btn",handler:function(){Ext.getCmp("newcaseform").getForm().reset();try{if(Ext.getCmp("newcase-win")){Ext.getCmp("newcase-win").close()}else{window.location="tr_show_product.cgi"}}catch(D){}}}]});Ext.getCmp("comp_product_combo").on("select",function(F,E,D){Ext.getCmp("component_picker").store.baseParams.product_id=E.get("id");Ext.getCmp("component_picker").store.load()});Ext.getCmp("component_picker").getSelectionModel().on("rowselect",function(D,E,F){Ext.getCmp("compfield").setValue(getSelectedObjects(Ext.getCmp("component_picker"),"id"));Ext.getCmp("default_tester").setValue(F.get("qa"))});Ext.getCmp("ncf_tabs").on("tabchange",function(D,E){E.doLayout()})};Ext.extend(NewCaseForm,Ext.form.FormPanel);CasePlans=function(A,D){var C=new TestopiaUtil();this.remove=function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"unlink",plan_id:getSelectedObjects(Ext.getCmp("case_plan_grid"),"plan_id"),case_id:A},success:function(){E.load()},failure:testopiaError})};this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",baseParams:{action:"getplans",case_id:A},root:"plans",id:"plan_id",fields:[{name:"plan_id",mapping:"plan_id"},{name:"plan_name",mapping:"plan_name"}]});var E=this.store;this.columns=[{header:"ID",dataIndex:"plan_id",hideable:false,renderer:C.planLink},{header:"Name",width:150,dataIndex:"plan_name",id:"plan_name",sortable:true,hideable:false}];var F=new Ext.form.ComboBox({store:new TestPlanStore({product_id:D,viewall:1},false),loadingText:"Looking up plans...",id:"link_plan_combo",width:150,displayField:"name",valueField:"plan_id",typeAhead:true,triggerAction:"all",minListWidth:300,forceSelection:true,emptyText:"Choose a Plan..."});var B=new Ext.Button({icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Link to plan",handler:function(){var H=new Ext.form.BasicForm("testopia_helper_frm",{});H.submit({url:"tr_process_case.cgi",params:{action:"link",plan_ids:F.getValue(),case_id:A},success:function(){E.load()},failure:testopiaError})}});var G=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Unlink Selected Plans",handler:this.remove});CasePlans.superclass.constructor.call(this,{title:"Plans",split:true,layout:"fit",autoExpandColumn:"plan_name",collapsible:true,id:"case_plan_grid",loadMask:{msg:"Loading plans..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[F,B,G]});E.on("load",function(H,I,J){if(H.getCount()==1){G.disable()}else{G.enable()}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(CasePlans,Ext.grid.GridPanel,{onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Unlink Selected Plans",id:"plan_remove_mnu",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:B.remove},{text:"Go to Plan",handler:function(){window.location="tr_show_plan.cgi?plan_id="+B.getSelectionModel().getSelected().get("plan_id")}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}if(this.store.getCount()==1){Ext.getCmp("plan_remove_mnu").disable()}else{Ext.getCmp("plan_remove_mnu").enable()}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseClonePanel=function(A,C){var B=new PlanGrid({product_id:A},{id:"plan_clone_grid"});CaseClonePanel.superclass.constructor.call(this,{id:"case-clone-panel",layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[B]},{region:"center",xtype:"form",title:"Clone Options",id:"case_clone_frm",border:false,frame:true,autoScroll:true,bodyStyle:"padding: 10px",labelWidth:250,height:280,items:[{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",title:"Create a copy (Unchecking will create a link to selected plans)",id:"case_copy_method",collapsed:true,items:[{xtype:"hidden",id:"case_copy_plan_ids",name:"plan_ids"},{xtype:"hidden",id:"case_clone_product_id",value:A,name:"product_id"},{xtype:"checkbox",boxLabel:"Keep Author (unchecking will make you the author of copied cases)",hideLabel:true,name:"keep_author",checked:true},{xtype:"checkbox",boxLabel:"Keep Default Tester (unchecking will make you the default tester of copied cases)",hideLabel:true,name:"keep_tester",checked:true},{xtype:"checkbox",boxLabel:"Copy case document (action, expected results, etc.)",hideLabel:true,name:"copy_doc",checked:true},{xtype:"checkbox",boxLabel:"Copy Attachments",hideLabel:true,name:"copy_attachments"},{xtype:"checkbox",boxLabel:"Copy Tags",hideLabel:true,name:"copy_tags",checked:true},{xtype:"checkbox",boxLabel:"Copy components",hideLabel:true,name:"copy_comps",checked:true},{xtype:"checkbox",boxLabel:"Copy category to new product",hideLabel:true,disabled:true,id:"case_clone_category_box",name:"copy_category",checked:true}]}]}],buttons:[{text:"Submit",handler:function(){Ext.getCmp("case_copy_plan_ids").setValue(getSelectedObjects(Ext.getCmp("plan_clone_grid"),"plan_id"));var D=Ext.getCmp("case_clone_frm").getForm();var E=D.getValues();D.baseParams={};D.baseParams.action="clone";D.baseParams.ids=C;D.submit({url:"tr_list_cases.cgi",success:function(G,H){if(E.copy_cases){if(H.result.tclist.length==1){Ext.Msg.show({title:"Test Case Copied",msg:"Test case "+H.result.tclist[0]+" Copied from Case "+C+". Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(I){if(I=="yes"){window.location="tr_show_case.cgi?case_id="+H.result.tclist[0]}}})}else{Ext.Msg.show({title:"Test Case Copied",msg:H.result.tclist.length+' Test cases Copied successfully View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}}else{Ext.Msg.show({title:"Test Case(s) Linked",msg:"Test cases "+C+" Linked successfully",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}Ext.getCmp("case-clone-win").close();try{Ext.getCmp("case_plan_grid").store.reload()}catch(F){}},failure:testopiaError})}},{text:"Cancel",handler:function(){try{Ext.getCmp("case-clone-win").close()}catch(D){window.location="tr_show_product.cgi"}}}]})};Ext.extend(CaseClonePanel,Ext.Panel);caseClonePopup=function(C,D){var F=new Ext.Window({id:"case-clone-win",closable:true,width:800,height:550,plain:true,shadow:false,layout:"fit",items:[new CaseClonePanel(C,D)]});var G=Ext.getCmp("plan_clone_grid");Ext.apply(G,{title:"Select plans to clone cases to"});F.show(this);var A=G.getTopToolbar().items.items;for(var B=0;B"+H[F].bug_id+", "}}return G}}];this.view=new Ext.grid.GroupingView({forceFit:true,groupTextTpl:'{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',enableRowBody:true,getRowClass:function(E,H,G,F){G.body="

Summary: "+E.data.case_summary+"

";return"x-grid3-row-expanded"}});this.tbar=[new Ext.Toolbar.Fill(),{xtype:"button",id:"save_caserun_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(E,F){saveSearch("caserun",Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}},{xtype:"button",id:"link_case_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(E,F){linkPopup(Ext.getCmp(A.id||"caserun_list_grid").store.baseParams)}}];CaseRunListGrid.superclass.constructor.call(this,{id:A.id||"caserun_list_grid",title:"Case Run History",loadMask:{msg:"Loading Test Cases..."},layout:"fit",region:"center",stripeRows:true,autoExpandColumn:"caserun_list_build_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});Ext.apply(this,A);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunListGrid,Ext.grid.GridPanel,{deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",single:true,ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRunGrid=function(H,G){H.limit=Ext.state.Manager.get("TESTOPIA_DEFAULT_PAGE_SIZE",25);var B=new TestopiaUtil();this.params=H;this.run=G;var A=new Ext.form.BasicForm("testopia_helper_frm",{});var D;this.summary_sort=function(){this.store.sortInfo.field="summary";this.store.sortInfo.direction=="DESC"?this.store.sortInfo.direction="ASC":this.store.sortInfo.direction="DESC";this.getView().mainHd.select("td").removeClass(this.getView().sortClasses);this.store.load()};envRenderer=function(J,O,M,I,K,L){var N=this.getColumnModel().getCellEditor(K,I).field;record=N.store.getById(J);if(record){return''+record.data[N.displayField]+""}else{return''+J+""}};this.store=new Ext.data.GroupingStore({url:"tr_list_caseruns.cgi",baseParams:H,reader:new Ext.data.JsonReader({totalProperty:"totalResultsAvailable",root:"Result",id:"caserun_id",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"sortkey",mapping:"sortkey"},{name:"case_id",mapping:"case_id"},{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"env_id",mapping:"env_id"},{name:"assignee",mapping:"assignee_name"},{name:"testedby",mapping:"testedby"},{name:"status",mapping:"status"},{name:"requirement",mapping:"requirement"},{name:"category",mapping:"category"},{name:"priority",mapping:"priority"},{name:"close_date",mapping:"close_date"},{name:"bug_count",mapping:"bug_count"},{name:"case_summary",mapping:"case_summary"},{name:"type",mapping:"type"},{name:"id",mapping:"id"},{name:"component",mapping:"component"},{name:"bug_list",mapping:"bug_list"}]}),remoteSort:true,sortInfo:{field:"sortkey",direction:"ASC"},groupField:"run_id"});var F=this.store;F.paramNames.sort="order";F.on("beforeload",function(I,J){I.baseParams.ctype="json"});var C=new BuildCombo({id:"tb_build",width:100,fieldLabel:"Build",hiddenName:"build",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,activeonly:1}});var E=new EnvironmentCombo({id:"tb_environment",width:100,fieldLabel:"Environment",hiddenName:"environment",mode:"remote",forceSelection:false,allowBlank:false,typeAhead:true,disabled:true,params:{product_id:G.plan.product_id,isactive:1}});C.on("select",function(K,J,I){H={build_id:J.get("id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});E.on("select",function(K,J,I){H={env_id:J.get("environment_id"),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")};TestopiaUpdateMultiple("caserun",H,Ext.getCmp("caserun_grid"))});this.object_type="environment";this.columns=[{header:"Case",width:50,dataIndex:"case_id",sortable:true,renderer:B.caseLink},{header:"Run",width:50,dataIndex:"run_id",sortable:true,renderer:B.runLink,hidden:true},{header:"Index",width:50,dataIndex:"sortkey",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.NumberField())},{header:"Build",width:50,dataIndex:"build",sortable:true,editor:new Ext.grid.GridEditor(new BuildCombo({params:{product_id:G.plan.product_id,activeonly:1}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Environment",width:50,dataIndex:"environment",sortable:true,editor:new Ext.grid.GridEditor(new EnvironmentCombo({params:{product_id:G.plan.product_id,isactive:1}})),renderer:envRenderer.createDelegate(this)},{header:"Assignee",width:150,sortable:true,dataIndex:"assignee",editor:new Ext.grid.GridEditor(new UserLookup({id:"caserun_assignee"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Tested By",width:150,sortable:true,dataIndex:"testedby",hidden:true},{header:"Closed",width:90,sortable:true,dataIndex:"close_date"},{header:"Status",width:30,sortable:true,dataIndex:"status",align:"center",renderer:B.statusIcon},{header:"Priority",width:60,sortable:true,dataIndex:"priority",editor:new Ext.grid.GridEditor(new PriorityCombo({id:"caserun_priority"})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Category",width:100,sortable:true,dataIndex:"category",editor:new Ext.grid.GridEditor(new CaseCategoryCombo({id:"caserun_category",params:{product_id:G.plan.product_id}})),renderer:TestopiaComboRenderer.createDelegate(this)},{header:"Requirement",width:150,sortable:true,dataIndex:"requirement",hidden:true},{header:"Component",width:100,sortable:true,dataIndex:"component"},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(I){var L=I.bugs;var K="";for(var J=0;J"+L[J].bug_id+", "}}return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("caserun",this.store);this.tbar=new Ext.Toolbar({id:"caserun_grid_tb",items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",disabled:true,handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",disabled:true,handler:function(){var I=0;var L=1;var K=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var J=0;JFAILED = REOPENED
PASSED = VERIFIED

"}),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),C,new Ext.Toolbar.Spacer(),E,new Ext.Toolbar.Spacer(),new Ext.Toolbar.Separator(),new Ext.Toolbar.Spacer(),new Ext.Toolbar.Fill(),{xtype:"button",id:"add_case_to_run_btn",tooltip:"Add cases to this run",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:function(){B.addCaseToRunPopup(G)}},{xtype:"button",id:"new_case_to_run_btn",tooltip:"Create a new case and add it to this run",icon:"testopia/img/new.png",iconCls:"img_button_16x",handler:function(){B.newCaseForm(G.plan_id,G.product_id,G.run_id)}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_edit_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Test Case",handler:function(){editFirstSelection(Ext.getCmp("caserun_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"caserun_grid_delete_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Remove Selected Test Cases from This Run",handler:this.deleteList.createDelegate(this)},new RunProgress({id:"run_progress",text:"0%",width:100})]});CaseRunGrid.superclass.constructor.call(this,{region:"center",id:"caserun_grid",border:false,bodyBorder:false,height:"400",stripeRows:true,split:true,enableDragDrop:true,loadMask:{msg:"Loading Test Cases..."},autoExpandColumn:"case_summary",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowdeselect:function(M,L,K){if(M.getCount()<1){Ext.getCmp("case_details_panel").disable();Ext.getCmp("tb_build").disable();Ext.getCmp("tb_environment").disable();Ext.getCmp("update_bugs").disable();var I=this.grid.getTopToolbar().items.items;for(var J=0;J1){return }Ext.getCmp("case_bugs_panel").tcid=L.get("case_id");Ext.getCmp("case_comps_panel").tcid=L.get("case_id");Ext.getCmp("attachments_panel").object=L.data;Ext.getCmp("case_details_panel").caserun_id=L.get("caserun_id");Ext.getCmp("casetagsgrid").obj_id=L.get("case_id");var K=Ext.getCmp("caserun_center_region").getActiveTab();Ext.getCmp(K.id).fireEvent("activate");if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}if(Ext.getCmp("case_bugs_panel")){Ext.getCmp("case_bugs_panel").case_id=L.get("case_id")}Ext.getCmp("case_details_panel").store.load({params:{caserun_id:L.get("caserun_id"),action:"gettext"}});D=N}}}),viewConfig:{forceFit:true,enableRowBody:true,getRowClass:function(I,L,K,J){K.body="

Summary: "+I.data.case_summary+"

";return"x-grid3-row-expanded"}}});this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(CaseRunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"caserun-ctx-menu",items:[{text:"Change",icon:"testopia/img/edit.png",iconCls:"img_button_16x",menu:{items:[{text:"Build",handler:function(){var D=new Ext.Window({title:"Edit Build",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new BuildCombo({params:{product_id:B.run.plan.product_id,activeonly:1},fieldLabel:"Build",id:"multi_build"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"build_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("build_applyall").getValue(),build_id:Ext.getCmp("multi_build").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Environment",handler:function(){var D=new Ext.Window({title:"Edit Environment",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new EnvironmentCombo({params:{product_id:B.run.plan.product_id,isactive:1},fieldLabel:"Environment",id:"multi_env"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"env_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("env_applyall").getValue(),env_id:Ext.getCmp("multi_env").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Priority",handler:function(){var D=new Ext.Window({title:"Edit Priority",id:"priority-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new PriorityCombo({fieldLabel:"Priority",id:"multi_priority"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,priority:Ext.getCmp("multi_priority").getValue(),ids:getSelectedObjects(B,"case_id")};TestopiaUpdateMultiple("case",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Category",handler:function(){var D=new Ext.Window({title:"Edit Category",id:"status-win",plain:true,shadow:false,width:300,height:150,items:[new CaseCategoryCombo({fieldLabel:"Category",params:{product_id:run.product_id}})],buttons:[{text:"Submit",handler:function(){TestopiaUpdateMultiple("case",{category:Ext.getCmp("case_category_combo").getValue(),ids:getSelectedObjects(B,"case_id")},B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}},{text:"Assignee",handler:function(){var D=new Ext.Window({title:"Edit Assignee",id:"status-win",plain:true,shadow:false,width:320,height:150,layout:"form",bodyStyle:"padding: 5px",items:[new UserLookup({fieldLabel:"Assignee",id:"multi_assignee"}),new Ext.form.Checkbox({fieldLabel:"Apply to all cases in this run",id:"assignee_applyall"})],buttons:[{text:"Submit",handler:function(){params={run_id:B.run.run_id,applyall:Ext.getCmp("assignee_applyall").getValue(),assignee:Ext.getCmp("multi_assignee").getValue(),ids:getSelectedObjects(B,"caserun_id")};TestopiaUpdateMultiple("caserun",params,B);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show(this)}}]}},{text:"Remove Selected Cases",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Add or Remove Tags",handler:function(){TagsUpdate("case",B)}},{text:"New Test Run",id:"addRun",handler:function(){window.location="tr_new_run.cgi?plan_id="+run.plan_id}},{text:"Clone Run with Selected Cases",handler:function(){RunClonePopup(B.run.product_id,B.run.run_id,getSelectedObjects(B,"case_id"))}},{text:"Copy or Link Selected Test Cases to Plan(s)... ",handler:function(){var D=B.getSelectionModel().getSelected();caseClonePopup(B.run.product_id,getSelectedObjects(B,"case_id"))}},{text:"Add Selected Test Cases to Run... ",handler:function(){Ext.Msg.prompt("Add to runs","",function(D,E){if(D=="ok"){TestopiaUpdateMultiple("case",{addruns:E,ids:getSelectedObjects(B,"case_id")},B)}})}},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Case in a New Window",handler:function(){window.open("tr_show_case.cgi?case_id="+B.store.getAt(B.selindex).get("case_id"))}},{text:"List These Test Cases in a New Window",handler:function(){var D=Ext.getCmp("caserun_search").form.getValues();if(D){window.open("tr_list_cases.cgi?"+jsonToSearch(D,"",["current_tab"])+"&isactive=1")}else{window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={caserun_id:B.record.get("caserun_id")};var C=this.store;switch(B.field){case"sortkey":A.action="update_sortkey";A.sortkey=B.value;break;case"build":A.action="update_build";A.build_id=B.value;break;case"environment":A.action="update_environment";A.caserun_env=B.value;break;case"assignee":A.action="update_assignee";A.assignee=B.value;break;case"priority":A.action="update_priority";A.priority=B.value;break;case"category":A.action="update_category";A.category=B.value;break}this.form.submit({url:"tr_caserun.cgi",params:A,success:function(E,D){if(D.result.caserun){var F=B.grid.store.reader.readRecords({Result:[D.result.caserun]}).records[0];B.grid.store.insert(B.row,F);C.commitChanges();B.grid.store.remove(B.record);B.grid.getSelectionModel().selectRow(B.row)}else{C.commitChanges()}},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;if(A.getSelectionModel().getCount()<1){return }Ext.Msg.show({title:"Confirm Delete?",msg:CASERUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"caserun-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_caseruns.cgi",params:{caserun_ids:getSelectedObjects(A,"caserun_id"),action:"delete",ctype:"json"},success:function(D){Ext.Msg.show({msg:"Test cases removed",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});CaseRun=function(){var B=new TestopiaUtil();this.caserun_id;this.store=new Ext.data.Store({url:"tr_caserun.cgi",baseParams:{action:"gettext"},reader:new Ext.data.XmlReader({record:"casetext",id:"case_id"},[{name:"action",mapping:"action"},{name:"results",mapping:"effect"},{name:"setup",mapping:"setup"},{name:"breakdown",mapping:"breakdown"},{name:"case_id",mapping:"case_id"},{name:"summary",mapping:"summary"},{name:"notes",mapping:"notes"}])});var A=this.store;A.on("load",function(D,E){Ext.getCmp("action_editor").setValue(E[0].get("action"));Ext.getCmp("effect_editor").setValue(E[0].get("results"));Ext.getCmp("setup_editor").setValue(E[0].get("setup"));Ext.getCmp("breakdown_editor").setValue(E[0].get("breakdown"));Ext.getCmp("summary_tb").items.items[7].td.innerHTML='Case '+E[0].get("case_id")+" - "+E[0].get("summary")});appendNote=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});D.submit({url:"tr_list_caseruns.cgi",params:{action:"update",note:Ext.getCmp("caserun_append_note_fld").getValue(),ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},success:function(){Ext.getCmp("caserun_append_note_fld").reset();A.reload()},failure:testopiaError})};processText=function(){var D=new Ext.form.BasicForm("testopia_helper_frm",{});var E={};E.tcsetup=Ext.getCmp("setup_editor").getValue();E.tcbreakdown=Ext.getCmp("breakdown_editor").getValue();E.tcaction=Ext.getCmp("action_editor").getValue();E.tceffect=Ext.getCmp("effect_editor").getValue();E.case_id=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("case_id");E.action="update_doc";D.submit({url:"tr_process_case.cgi",params:E,success:function(){TestopiaUtil.notify.msg("Test case updated","Test Case {0} was updated successfully","Document")},failure:testopiaError})};var C=new Ext.Toolbar({id:"summary_tb",disabled:true,items:[new Ext.Button({template:imgButtonTpl,text:"testopia/img/IDLE.gif",tooltip:"Mark as IDLE (Not Run)",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:1,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/PASSED.gif",tooltip:"Mark as PASSED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:2,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/FAILED.gif",tooltip:"Mark as FAILED",handler:function(){TestopiaUpdateMultiple("caserun",{status_id:3,ids:getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id"),update_bug:Ext.getCmp("update_bugs").getValue()},Ext.getCmp("caserun_grid"))}}),new Ext.Button({template:imgButtonTpl,text:"testopia/img/RUNNING.gif",tooltip:"Mark as RUNNING",handler:function(){var D=0;var G=1;var F=Ext.getCmp("caserun_grid").getSelectionModel().getSelections();for(var E=0;E','
{notes}
',"",'
')}],bbar:[new Ext.menu.TextItem("Add a Note: "),{xtype:"textfield",id:"caserun_append_note_fld",width:1000},{xtype:"button",text:"Append Note",handler:appendNote.createDelegate(this)}]},new CaseRunHistory(),new AttachGrid({id:0,type:"caserun"}),new CaseBugsGrid(),new CaseComponentsGrid(),new TestopiaObjectTags("case",0)]}]})};Ext.extend(CaseRun,Ext.Panel,this);CaseRunHistory=function(){var A=new TestopiaUtil();this.store=new Ext.data.JsonStore({url:"tr_caserun.cgi",baseParams:{action:"gethistory"},root:"records",fields:[{name:"caserun_id",mapping:"case_run_id"},{name:"build",mapping:"build_name"},{name:"environment",mapping:"env_name"},{name:"status",mapping:"status_name"},{name:"testedby",mapping:"testedby"},{name:"closed",mapping:"close_date"},{name:"isactive",mapping:"isactive"},{name:"bug_list",mapping:"bug_list"}]});this.columns=[{header:"Build",width:150,dataIndex:"build",sortable:true},{header:"Environment",width:150,dataIndex:"environment",sortable:true},{header:"Status",width:50,dataIndex:"status",sortable:true,renderer:A.statusIcon},{header:"Tested By",width:200,dataIndex:"testedby",sortable:true},{header:"Closed",width:150,dataIndex:"closed",sortable:true},{header:"Bugs In This Build and Environment",width:100,dataIndex:"bug_list",sortable:false,hideable:true,renderer:function(B){if(!B){return }var E=B.bugs;var D="";for(var C=0;C"+E[C].bug_id+", "}}return D}}];CaseRunHistory.superclass.constructor.call(this,{border:false,title:"History",id:"caserun_history_panel",bodyBorder:false,loadMask:{msg:"Loading Test Cases..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true})});this.on("activate",this.onActivate,this)};Ext.extend(CaseRunHistory,Ext.grid.GridPanel,{onActivate:function(A){this.store.load({params:{action:"gethistory",caserun_id:Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}})}});CaseBugsGrid=function(F){var C=new TestopiaUtil();var A=new Ext.form.BasicForm("testopia_helper_frm",{});function E(G){return''+G+""}var B;if(F){B=F}this.tcid=B;this.store=new Ext.data.JsonStore({url:"tr_process_case.cgi",root:"bugs",baseParams:{action:"getbugs"},fields:[{name:"run_id",mapping:"run_id"},{name:"build",mapping:"build"},{name:"env",mapping:"env"},{name:"summary",mapping:"summary"},{name:"case_run_id",mapping:"case_run_id"},{name:"bug_id",mapping:"bug_id"},{name:"status",mapping:"status"},{name:"resolution",mapping:"resolution"},{name:"assignee",mapping:"assignee"},{name:"severity",mapping:"severity"},{name:"priority",mapping:"priority"}]});addbug=function(){B=this.tcid;var H;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";H=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{H=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bug_action:"attach",bugs:Ext.getCmp("attachbug").getValue(),type:G,ids:H},success:function(){D.load({params:{case_id:B}});Ext.getCmp("attachbug").reset()},failure:testopiaError})};removebug=function(){B=this.tcid;var G="case";if(Ext.getCmp("caserun_grid")){G="caserun";ids=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}else{ids=B}A.submit({url:"tr_list_cases.cgi",params:{action:"update_bugs",bugs:getSelectedObjects(Ext.getCmp("case_bugs_panel"),"bug_id"),type:G,ids:ids},success:function(){D.load({params:{case_id:B}})},failure:testopiaError})};newbug=function(){var G=new Ext.Panel({id:"new_bug_panel"});var I;if(Ext.getCmp("caserun_grid")&&Ext.getCmp("caserun_grid").getSelectionModel().getCount()){I=Ext.getCmp("caserun_grid").getSelectionModel().getSelected().get("caserun_id")}var H=new Ext.data.Store({url:"tr_process_case.cgi",baseParams:{action:"case_to_bug",case_id:this.tcid,caserun_id:I},reader:new Ext.data.XmlReader({record:"newbug",id:"case_id"},[{name:"product",mapping:"product"},{name:"version",mapping:"version"},{name:"component",mapping:"component"},{name:"comment",mapping:"comment"},{name:"case_id",mapping:"case_id"},{name:"assigned_to",mapping:"assigned_to"},{name:"qa_contact",mapping:"qa_contact"},{name:"short_desc",mapping:"short_desc"}])});H.load();H.on("load",function(){var J="enter_bug.cgi?";for(var K=0;K';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+'
';K=K+'
'+I+"
";K=K+"
";K=K+"
";K=K+"";return K}}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("run",this.store);RunGrid.superclass.constructor.call(this,{title:"Test Runs",id:B.id||"run_grid",loadMask:{msg:"Loading Test Runs..."},autoExpandColumn:"run_summary",autoScroll:true,stripeRows:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false,listeners:{rowselect:function(J,H,I){Ext.getCmp("new_case_to_run_button").enable();Ext.getCmp("delete_run_list_btn").enable();Ext.getCmp("edit_run_list_btn").enable()},rowdeselect:function(J,H,I){if(J.getCount()<1){Ext.getCmp("new_case_to_run_button").disable();Ext.getCmp("delete_run_list_btn").disable();Ext.getCmp("edit_run_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Add Test Cases to Selected Runs",id:"new_case_to_run_button",disabled:true,handler:function(){var H=Ext.getCmp(B.id||"run_grid").getSelectionModel().getSelected();D.addCaseToRunPopup(H)}},new Ext.Toolbar.Fill(),{xtype:"button",id:"save_run_list_btn",icon:"testopia/img/save.png",iconCls:"img_button_16x",tooltip:"Save this search",handler:function(H,I){saveSearch("run",Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"link_run_list_btn",icon:"testopia/img/link.png",iconCls:"img_button_16x",tooltip:"Create a link to this list",handler:function(H,I){linkPopup(Ext.getCmp(B.id||"run_grid").store.baseParams)}},{xtype:"button",id:"edit_run_list_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",disabled:true,tooltip:"Edit Selected Test Run",handler:function(){editFirstSelection(Ext.getCmp(B.id||"run_grid"))}},{xtype:"button",id:"add_run_list_btn",icon:"testopia/img/new.png",iconCls:"img_button_16x",tooltip:"Create a New Test Run",handler:function(){try{if(plan){D.newRunPopup(plan)}}catch(H){window.location="tr_new_run.cgi"}}},{xtype:"button",id:"delete_run_list_btn",icon:"testopia/img/delete.png",iconCls:"img_button_16x",disabled:true,tooltip:"Delete Selected Test Runs",handler:this.deleteList.createDelegate(this)}]});Ext.apply(this,B);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(RunGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){B.selindex=A;if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Run Status Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=status&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=completion&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Execution Report",handler:function(){var D=new Ext.Window({title:"Select a date range",id:"run_execution_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[{xtype:"datefield",id:"execution_start_date",fieldLabel:"Start Date",name:"chfieldfrom"},{xtype:"datefield",fieldLabel:"Stop Date",id:"execution_stop_date",emptyText:"Now",name:"chfieldto"},new UserLookup({id:"exec_tester",fieldLabel:"Tester (optional)"})]})],buttons:[{text:"Submit",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var E=new Ext.ux.Portlet({title:"Execution Report",closable:true,autoScroll:true,tools:PortalTools});E.url="tr_run_reports.cgi?type=execution&run_ids="+getSelectedObjects(B,"run_id")+"&chfieldfrom="+Ext.getCmp("execution_start_date").getValue()+"&chfieldto="+Ext.getCmp("execution_stop_date").getValue()+"&tester="+Ext.getCmp("exec_tester").getValue();Testopia.Search.dashboard_urls.push(E.url);Ext.getCmp("dashboard_leftcol").add(E);Ext.getCmp("dashboard_leftcol").doLayout();E.load({url:E.url});D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"New Priority Breakdown Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Status Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=priority&run_ids="+getSelectedObjects(B,"run_id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}},{text:"New Run Bug Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Bug Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_run_reports.cgi?type=bug_grid&run_ids="+getSelectedObjects(B,"run_id")+"&noheader=1";Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({scripts:true,url:D.url})}}]}},{text:"Edit",menu:{items:[{text:"Manager",handler:function(){var D=new Ext.Window({title:"Change Run Manager",id:"run_manager_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new UserLookup({id:"manager_update",fieldLabel:"Run Manager"})]})],buttons:[{text:"Update Manager",handler:function(){TestopiaUpdateMultiple("run",{manager:Ext.getCmp("manager_update").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}},{text:"Tags",handler:function(){TagsUpdate("run",B)}},{text:"Targets",handler:function(){var D=new Ext.Window({title:"Change Run Targets",id:"run_target_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({bodyStyle:"padding: 5px",items:[new Ext.form.NumberField({maxValue:100,minValue:0,id:"target_completion",allowBlank:true,fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(E){Ext.getCmp("target_pass").maxValue=E.getValue()}}}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]})],buttons:[{text:"Update Targets",handler:function(){TestopiaUpdateMultiple("run",{target_pass:Ext.getCmp("target_pass").getValue(),target_completion:Ext.getCmp("target_completion").getValue(),ids:getSelectedObjects(B,"run_id")},B);D.close()}},{text:"Cancel",handler:function(){D.close()}}]});D.show()}}]}},{text:"Clone Selected Test Runs",icon:"testopia/img/copy.png",iconCls:"img_button_16x",handler:function(){RunClonePopup(B.getSelectionModel().getSelected().get("product_id"),getSelectedObjects(B,"run_id"))}},{text:"Delete Selected Test Runs",icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.deleteList.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}},{text:"View Test Run in a New Window",handler:function(){window.open("tr_show_run.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}},{text:"View Run's Test Cases in a New Window",handler:function(){window.open("tr_list_cases.cgi?run_id="+B.store.getAt(B.selindex).get("run_id"))}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(B){var A={action:"edit",run_id:B.record.get("run_id")};var C=this.store;switch(B.field){case"product_version":A.run_product_version=B.value;break;case"manager":A.manager=B.value;break;case"build":A.build=B.value;break;case"environment":A.environment=B.value;break;case"summary":A.summary=B.value;break}this.form.submit({url:"tr_process_run.cgi",params:A,success:function(E,D){C.commitChanges()},failure:function(E,D){testopiaError(E,D);C.rejectChanges()}})},deleteList:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:RUN_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"run-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(C){if(C=="yes"){var B=new Ext.form.BasicForm("testopia_helper_frm");B.submit({url:"tr_list_runs.cgi",params:{run_ids:getSelectedObjects(A,"run_id"),action:"delete"},success:function(D){Ext.Msg.show({msg:"Test runs deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(E,D){testopiaError(E,D);A.store.reload()}})}}})},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});var NewRunForm=function(A){if(A.data){A=A.data}var B=new CaseGrid({plan_id:A.plan_id,case_status:"CONFIRMED"},{title:"Select From Existing Cases",region:"center",id:"newrun_casegrid",height:500});this.casegrid=B;B.on("render",function(D){for(var C=0;CProduct Version",hiddenName:"prod_version",mode:"local",forceSelection:true,allowBlank:false,typeAhead:true,params:{product_id:A.product_id}}),new UserLookup({id:"new_run_manager",hiddenName:"manager",fieldLabel:"Run Manager",allowBlank:false}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_completion",fieldLabel:"Target Completion Rate",hiddenName:"target_completion",listeners:{valid:function(C){Ext.getCmp("target_pass").maxValue=C.getValue()}}})]},{columnWidth:0.5,layout:"form",items:[new BuildCombo({fieldLabel:"Build",hiddenName:"build",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id,activeonly:1},emptyText:"Select or type a new name"}),new EnvironmentCombo({fieldLabel:"Environment",hiddenName:"environment",mode:"local",forceSelection:false,allowBlank:false,typeAhead:true,params:{product_id:A.product_id},emptyText:"Select or type a new name"}),new Ext.form.NumberField({maxValue:100,minValue:0,allowBlank:true,id:"target_pass",fieldLabel:"Target Pass Rate",hiddenName:"target_pass"})]}]},{xtype:"textfield",fieldLabel:"Summary",layout:"fit",id:"run_summary",name:"summary",anchor:"100%",width:600,allowBlank:false},{xtype:"hidden",name:"plan_id",value:A.plan_id},{layout:"fit",fieldLabel:"Notes",id:"notes",xtype:"textarea",width:600,height:80}]}],buttons:[{text:"Create New Case",handler:function(){var C=new TestopiaUtil();C.newCaseForm(A.plan_id,A.product_id)}},{text:"Submit",handler:function(){if(!Ext.getCmp("newrunsouth").getForm().isValid()){return }var C={action:"add"};if(Ext.getCmp("selectall").getValue()){C.getall=Ext.getCmp("selectall").getValue()?1:0}else{C.case_ids=getSelectedObjects(B,"case_id")}if(!Ext.getCmp("build_combo").getValue()){C.new_build=Ext.getCmp("build_combo").getRawValue()}if(!Ext.getCmp("environment_combo").getValue()){C.new_env=Ext.getCmp("environment_combo").getRawValue()}Ext.getCmp("newrunsouth").getForm().submit({params:C,success:function(D,E){Ext.Msg.show({title:"Test Run Created",msg:"Test run "+E.result.run_id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(F){if(F=="yes"){window.location="tr_show_run.cgi?run_id="+E.result.run_id}}});if(Ext.getCmp("plan_run_grid")){Ext.getCmp("plan_run_grid").store.reload()}},failure:testopiaError})}},{text:"Cancel",type:"reset",id:"nrf_cancel_btn",handler:function(){Ext.getCmp("newrunsouth").getForm().reset();try{Ext.getCmp("newRun-win").close()}catch(C){window.location="tr_show_product.cgi"}}}]});this.on("render",function(){B.store.load();Ext.getCmp("new_run_manager").setValue(Testopia_user.login)})};Ext.extend(NewRunForm,Ext.Panel);RunClonePanel=function(D,H,B){var E=new PlanGrid({product_id:D},{id:"run_clone_plan_grid"});var C=new ProductVersionCombo({id:"run_clone_version_chooser",mode:"local",hiddenName:"new_run_prod_version",fieldLabel:"Product Version",params:{product_id:D}});var G=new BuildCombo({fieldLabel:"Select a Build",id:"run_clone_build_chooser",mode:"local",hiddenName:"new_run_build",params:{product_id:D,activeonly:1}});var A=new EnvironmentCombo({fieldLabel:"Select an Environment",id:"run_clone_environment_chooser",mode:"local",hiddenName:"new_run_env",params:{product_id:D}});function F(){var I=Ext.getCmp("run_clone_frm").getForm();I.baseParams={};if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_filtered_cases"){I.baseParams=Ext.getCmp("caserun_search").form.getValues()}else{if(Ext.getCmp("copy_cases_radio_group").getGroupValue()=="copy_selected_cases"){I.baseParams.case_list=getSelectedObjects(Ext.getCmp("caserun_grid"),"caserun_id")}}I.baseParams.action="clone";I.baseParams.ids=H;I.baseParams.new_run_build=G.getValue();I.baseParams.new_run_environment=A.getValue();I.baseParams.plan_ids=getSelectedObjects(E,"plan_id");var J=I.getValues();if(I.isValid()){I.submit({success:function(L,K){var M;if(K.result.runlist.length==1){M=K.result.failures.length>0?"Test cases "+K.result.failures.join(",")+" were not included. They are either DISABLED or PROPOSED.
":"";Ext.Msg.show({title:"Run Copied",msg:M+"Run "+K.result.runlist[0]+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(N){if(N=="yes"){window.location="tr_show_run.cgi?run_id="+K.result.runlist[0]}}})}else{M=K.result.failures.length>0?K.result.failures.join.length+' Test cases were not included. They are either DISABLED or PROPOSED. View List
':"";Ext.Msg.show({title:"Test Run Copied",msg:M+K.result.runlist.length+' Test runs Copied successfully. View List',buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO})}},failure:testopiaError})}}RunClonePanel.superclass.constructor.call(this,{id:"run_clone_form",border:false,width:600,layout:"border",items:[{region:"north",layout:"fit",border:false,height:300,items:[E]},{region:"center",xtype:"form",url:"tr_list_runs.cgi",title:"Clone Options",autoScroll:true,id:"run_clone_frm",border:false,frame:true,bodyStyle:"padding: 10px",labelWidth:160,height:350,items:[{layout:"table",border:false,autoScroll:true,layoutConfig:{columns:2,width:"100%"},items:[{colspan:2,layout:"form",border:false,items:[{id:"run_clone_name",xtype:"textfield",fieldLabel:"New Run Summary",name:"new_run_summary",width:500}]},{layout:"form",border:false,items:[C,G,A]},{layout:"form",border:false,items:[{xtype:"checkbox",name:"copy_tags",checked:true,boxLabel:"Copy Run Tags",hideLabel:true},{xtype:"hidden",id:"run_clone_product_id",name:"product_id",value:D}]},{colspan:2,layout:"form",border:false,items:[{xtype:"checkbox",name:"keep_run_manager",checked:false,boxLabel:"Maintain original manager (unchecking will make me the manager of the new run)",hideLabel:true},{xtype:"fieldset",autoHeight:true,checkboxToggle:true,checkboxName:"copy_cases",id:"run_copy_cases",title:"Copy Test Cases",collapsed:B?false:true,items:[{xtype:"radio",name:"copy_cases_options",id:"copy_cases_radio_group",inputValue:"copy_all_cases",checked:true,boxLabel:"Include all CONFIRMED cases in selected run(s)",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_filtered_cases",boxLabel:"Only include cases that match the selected filter",hideLabel:true},{xtype:"radio",name:"copy_cases_options",inputValue:"copy_selected_cases",boxLabel:"Only include cases that are currently selected",checked:B?true:false,hideLabel:true},{xtype:"checkbox",name:"keep_indexes",checked:true,boxLabel:"Copy Case Indexes",hideLabel:true},{xtype:"checkbox",name:"keep_statuses",boxLabel:"Maintain status of copied cases (unchecking will set case copies to IDLE (Not Run))",hideLabel:true}]}]}]}]}],buttons:[{text:"Submit",handler:F.createDelegate(this)},{text:"Cancel",handler:function(){Ext.getCmp("run-clone-win").close()}}]})};Ext.extend(RunClonePanel,Ext.Panel);RunClonePopup=function(D,G,A){var F=new Ext.Window({id:"run-clone-win",closable:true,width:800,height:600,plain:true,shadow:false,layout:"fit",items:[new RunClonePanel(D,G,A)]});var H=Ext.getCmp("run_clone_plan_grid");Ext.apply(H,{title:"Select plans to clone runs to"});F.show(this);var B=H.getTopToolbar().items.items;for(var C=0;C 1 ? "Items" : "Item"]})'});this.columns=[{header:"Run",dataIndex:"run_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.runLink},{header:"Case",dataIndex:"case_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.caseLink},{header:"Bug",dataIndex:"bug_id",sortable:true,hideable:true,groupRenderer:function(C){return C},renderer:A.bugLink},{header:"Bug Status",dataIndex:"bug_status",sortable:true,hideable:true},{header:"Case Status",dataIndex:"case_status",sortable:true,hideable:true},{header:"Severity",dataIndex:"severity",sortable:true,hideable:true}];Testopia.BugReport.superclass.constructor.call(this,{sm:new Ext.grid.RowSelectionModel(),layout:"fit",height:250,autoScroll:true})};Ext.extend(Testopia.BugReport,Ext.grid.GridPanel);BuildGrid=function(A){this.product_id=A;this.store=new BuildStore({},false);var B=new MilestoneCombo({hiddenField:"milestone",mode:"remote",params:{product_id:A}});this.columns=[{header:"Name",width:80,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Milestone",width:120,sortable:true,dataIndex:"milestone",editor:new Ext.grid.GridEditor(B,{listeners:{startedit:function(){var C=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().id;if(B.store.baseParams.product_id!=C){B.store.baseParams.product_id=C;B.store.load()}}}})},{header:"Description",width:120,editor:new Ext.grid.GridEditor(new Ext.form.TextField()),sortable:true,dataIndex:"description"},new Ext.grid.CheckColumn({header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm");BuildGrid.superclass.constructor.call(this,{title:"Builds",id:"build_grid",loadMask:{msg:"Loading Builds..."},autoExpandColumn:"build_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_build_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Build",handler:function(){editFirstSelection(Ext.getCmp("build_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_build_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Build",handler:this.newRecord}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(BuildGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewBuild=Ext.data.Record.create([{name:"name",type:"string"},{name:"milestone"},{name:"description",type:"string"},{name:"isactive",type:"bool"}]);var A=new NewBuild({name:"",milestone:Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone,description:"",isactive:true});var B=Ext.getCmp("build_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"build-ctx-menu",items:[{text:"Reports",menu:{items:[{text:"New Completion Report",handler:function(){Ext.getCmp("object_panel").setActiveTab("dashboardpanel");var D=new Ext.ux.Portlet({title:"Build Completion Report",closable:true,autoScroll:true,tools:PortalTools});D.url="tr_builds.cgi?action=report&product_id="+B.product_id+"&build_ids="+getSelectedObjects(B,"id");Testopia.Search.dashboard_urls.push(D.url);Ext.getCmp("dashboard_leftcol").add(D);Ext.getCmp("dashboard_leftcol").doLayout();D.load({url:D.url})}}]}},{text:"Add a Build",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Build",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("id");var A={product_id:this.product_id,build_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break;case"isactive":A.isactive=D.value;break;case"milestone":A.milestone=D.value;break}}else{A.action="add";A.name=D.value;A.milestone=Ext.getCmp("products_pane").getSelectionModel().getSelectedNode().attributes.attributes.defaultmilestone;A.isactive=1}this.form.submit({url:"tr_builds.cgi",params:A,success:function(F,E){if(E.result.build_id){D.record.set("id",E.result.build_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_build_btn").disable();Ext.getCmp("add_build_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});CaseCategoryGrid=function(A){this.product_id=A;this.store=new CaseCategoryStore({},false);var B=this.store;this.columns=[{header:"Name",width:120,sortable:true,dataIndex:"name",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"name",allowBlank:false}),{completeOnEnter:true,listeners:{beforecomplete:function(D,C){if(!D.getValue()){return false}}}})},{header:"Description",width:120,id:"category_desc_column",editor:new Ext.grid.GridEditor(new Ext.form.TextField({value:"description"})),sortable:true,dataIndex:"description"}];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});CaseCategoryGrid.superclass.constructor.call(this,{title:"Categories",id:"category_grid",loadMask:{msg:"Loading Categories..."},autoExpandColumn:"category_desc_column",autoScroll:true,enableColumnHide:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true}),viewConfig:{forceFit:true},tbar:[new Ext.Toolbar.Fill(),{xtype:"button",id:"edit_category_btn",icon:"testopia/img/edit.png",iconCls:"img_button_16x",tooltip:"Edit Selected Category",handler:function(){editFirstSelection(Ext.getCmp("category_grid"))}},{xtype:"button",template:button_16x_tmpl,id:"add_category_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add a new Category",handler:this.newRecord},{xtype:"button",template:button_16x_tmpl,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Category",handler:function(){var C=Ext.getCmp("category_grid").getSelectionModel().getSelected();if(!C){Ext.MessageBox.alert("Message","Please select at least one Category to delete")}else{confirmCaseCategoryDelete(A)}}}]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this);this.on("afteredit",this.onGridEdit,this)};Ext.extend(CaseCategoryGrid,Ext.grid.EditorGridPanel,{newRecord:function(){NewCategory=Ext.data.Record.create([{name:"name",type:"string"},{name:"description",type:"string"}]);var A=new NewCategory({name:"",description:""});var B=Ext.getCmp("category_grid");B.store.insert(0,A);B.startEditing(0,0)},onContextClick:function(B,A,C){B.getSelectionModel().selectRow(A);if(!this.menu){this.menu=new Ext.menu.Menu({id:"category-ctx-menu",items:[{text:"Add a Category",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.newRecord},{text:"Edit This Category",icon:"testopia/img/edit.png",iconCls:"img_button_16x",handler:function(){editFirstSelection(B)}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onGridEdit:function(D){var B=D.record.get("category_id");var A={product_id:this.product_id,category_id:B};var C=this.store;if(B){A.action="edit";switch(D.field){case"name":A.name=D.value;break;case"description":A.description=D.value;break}}else{A.action="add";A.name=D.value}this.form.submit({url:"tr_categories.cgi",params:A,success:function(F,E){if(E.result.category_id){D.record.set("category_id",E.result.category_id)}C.commitChanges()},failure:function(F,E){testopiaError(F,E);C.rejectChanges()}})},onActivate:function(A){if(!this.product_id){Ext.Msg.alert("Error","Please select a product.");Ext.getCmp("edit_category_btn").disable();Ext.getCmp("add_category_btn").disable();return }else{if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}}});confirmCaseCategoryDelete=function(){if(!Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id")){Ext.getCmp("category_grid").store.reload();return }Ext.Msg.show({title:"Confirm Delete?",msg:CASE_CATEGORY_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"casecategory-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){var A=new Ext.form.BasicForm("testopia_helper_frm");A.submit({url:"tr_categories.cgi",params:{category_id:Ext.getCmp("category_grid").getSelectionModel().getSelected().get("category_id"),action:"delete",product_id:Ext.getCmp("category_grid").product_id},success:function(C){Ext.Msg.show({msg:"Test case category deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});Ext.getCmp("category_grid").store.reload()},failure:testopiaError})}}})};EnvironmentGrid=function(E,A){this.params=E;this.product_id=E.product_id;function B(F){return''+F+""}function D(F){return''+F+""}this.store=new EnvironmentStore(E,false);var C=this.store;this.columns=[{header:"ID",width:30,dataIndex:"environment_id",sortable:true,renderer:B,hideable:false},{header:"Environment Name",width:110,dataIndex:"name",id:"env_name_col",sortable:true,editor:new Ext.grid.GridEditor(new Ext.form.TextField({allowBlank:false}),{id:"env_name_edt"})},{header:"Product Name",width:150,dataIndex:"product",sortable:true,hidden:true},{header:"Run Count",width:30,dataIndex:"run_count",sortable:false},new Ext.grid.CheckColumn({sortable:true,header:"Active",dataIndex:"isactive",editor:new Ext.grid.GridEditor(new Ext.form.Checkbox({value:"isactive"})),width:25})];this.form=new Ext.form.BasicForm("testopia_helper_frm",{});this.bbar=new TestopiaPager("environment",this.store);EnvironmentGrid.superclass.constructor.call(this,{title:"Environments",id:"environment-grid",loadMask:{msg:"Loading Environments..."},autoExpandColumn:"env_name_col",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:function(H,F,G){Ext.getCmp("delete_env_list_btn").enable();Ext.getCmp("clone_env_list_btn").enable()},rowdeselect:function(H,F,G){if(H.getCount()<1){Ext.getCmp("delete_env_list_btn").disable();Ext.getCmp("clone_env_list_btn").disable()}}}}),viewConfig:{forceFit:true},tbar:[{xtype:"button",text:"Import",handler:this.importEnv.createDelegate(this)},new Ext.Toolbar.Fill(),{xtype:"button",id:"add_env_list_btn",template:button_16x_tmpl,icon:"testopia/img/add.png",iconCls:"img_button_16x",tooltip:"Add an Environment",handler:this.createEnv.createDelegate(this,["","add"])},{xtype:"button",id:"clone_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/copy.png",iconCls:"img_button_16x",tooltip:"Clone this Environment",handler:this.cloneEnv.createDelegate(this)},{xtype:"button",id:"delete_env_list_btn",template:button_16x_tmpl,disabled:true,icon:"testopia/img/delete.png",iconCls:"img_button_16x",tooltip:"Delete this Environment",handler:this.deleteEnv.createDelegate(this)}]});Ext.apply(this,A);this.on("rowcontextmenu",this.onContextClick,this);this.on("afteredit",this.onGridEdit,this);this.on("activate",this.onActivate,this)};Ext.extend(EnvironmentGrid,Ext.grid.EditorGridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"run-ctx-menu",items:[{text:"Create a new environment",handler:function(){window.location="tr_new_environment.cgi"}},{text:"Delete Environments",handler:this.deleteEnv.createDelegate(this)},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onGridEdit:function(C){var A={env_id:C.record.get("environment_id")};var B=this.store;switch(C.field){case"name":A.action="rename";A.name=C.value;break;case"isactive":A.action="toggle";break}this.form.submit({url:"tr_environments.cgi",params:A,success:function(E,D){B.commitChanges()},failure:function(E,D){testopiaError(E,D);B.rejectChanges()}})},deleteEnv:function(){var A=this;Ext.Msg.show({title:"Confirm Delete?",msg:ENVIRONMENT_DELETE_WARNING,buttons:Ext.Msg.YESNO,animEl:"case-delete-btn",icon:Ext.MessageBox.QUESTION,fn:function(B){if(B=="yes"){form=new Ext.form.BasicForm("testopia_helper_frm",{});form.submit({url:"tr_environments.cgi",params:{env_id:A.getSelectionModel().getSelected().get("environment_id"),action:"delete"},success:function(){Ext.Msg.show({msg:"Test environment deleted",buttons:Ext.Msg.OK,icon:Ext.MessageBox.INFO});A.store.reload()},failure:function(D,C){testopiaError(D,C);A.store.reload()}})}}})},createEnv:function(A,C,E){var B=this;C=C||"add";var D=new Ext.Window({id:"create-env-win",title:"Environment XML Import",closable:true,width:400,height:230,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_environments.cgi",bodyStyle:"padding: 10px",id:"env_create_frm",items:[{xtype:"field",fieldLabel:"Name",inputType:"text",name:"name",value:A!=""?"Copy of "+A:"",allowBlank:false},new ProductCombo({mode:"local",fieldLabel:"Product",value:B.product_id,hiddenName:"product_id"}),{xtype:"hidden",name:"action",value:C},{xtype:"hidden",name:"env_id",value:E}],buttons:[{text:"Create",handler:function(){Ext.getCmp("env_create_frm").getForm().submit({success:function(F,G){Ext.Msg.show({title:"Test Environment Created",msg:"Test environment "+G.result.id+" Created. Would you like to go there now?",buttons:Ext.Msg.YESNO,icon:Ext.MessageBox.QUESTION,fn:function(H){if(H=="yes"){window.location="tr_environments.cgi?env_id="+G.result.id}else{B.store.reload()}}});Ext.getCmp("create-env-win").close()},failure:testopiaError})}},{text:"Cancel",handler:function(){Ext.getCmp("create-env-win").close()}}]}]});D.show(this)},cloneEnv:function(){this.createEnv(this.getSelectionModel().getSelected().get("name"),"clone",this.getSelectionModel().getSelected().get("environment_id"))},importEnv:function(){grid=this;var A=new Ext.Window({id:"import-env-win",title:"Environment XML Import",closable:true,width:400,height:130,plain:true,shadow:false,layout:"fit",items:[{xtype:"form",url:"tr_import_environment.cgi",bodyStyle:"padding: 10px",id:"env_xml_import_frm",fileUpload:true,items:[{xtype:"field",fieldLabel:"XML",inputType:"file",name:"xml",allowBlank:false}],buttons:[{text:"Import",handler:function(){Ext.getCmp("env_xml_import_frm").getForm().submit();Ext.getCmp("import-env-win").close();grid.store.reload()}},{text:"Cancel",handler:function(){Ext.getCmp("import-env-win").close()}}]}]});A.show(this)},onActivate:function(A){if(!this.store.getCount()){this.store.load()}}});Testopia.Search={};Testopia.Search.dashboard_urls=[];Testopia.Search.fillInForm=function(C,F,A){var E=document.getElementById(C+"_search_form");for(var B=0;B");var D;for(var F in H){if(typeof H[F]!="string"){continue}var B=searchToJson(H[F]);var J;typeof B.qname=="object"?J=B.qname[0]:J=B.qname;D=new Ext.ux.Portlet({title:J||" ",id:"search"+A.get("name")+F,closable:true,autoScroll:true,tools:PortalTools,url:H[F]});Ext.getCmp(I).add(D);Ext.getCmp(I).doLayout();I=I=="lc_"+A.get("name")?"rc_"+A.get("name"):"lc_"+A.get("name");D.load({scripts:true,url:H[F]})}}else{var E=searchToJson(A.get("query"));var C=E.current_tab;switch(C){case"plan":Ext.getCmp("object_panel").add(new PlanGrid(E,G));break;case"run":Ext.getCmp("object_panel").add(new RunGrid(E,G));break;case"case":Ext.getCmp("object_panel").add(new CaseGrid(E,G));break;default:Ext.Msg.show({title:"No Type Found",msg:"There must have been a problem saving this search. I can't find a type",buttons:Ext.Msg.OK,icon:Ext.MessageBox.ERROR});return }Ext.getCmp("object_panel").activate("search"+A.get("name"))}}});PortalTools=[{id:"gear",handler:function(D,C,A){var B=new Ext.form.BasicForm("testopia_helper_frm",{});this.menu=new Ext.menu.Menu({id:"portal_tools_menu",items:[{text:"Save",handler:function(){Ext.Msg.prompt("Save Report As","",function(E,F){if(E=="ok"){B.submit({url:"tr_query.cgi",params:{action:"save_query",query_name:F,query_part:A.url,type:1},success:function(){Ext.getCmp("reports_grid").store.load();A.title=F},failure:testopiaError})}})}},{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){A.load({url:A.url})}},{text:"Link to this report",handler:function(){var H;if(A.url.match(/^http/)){H=A.url;H=H.replace(/\&noheader=1/gi,"")}else{var E=window.location;var F=E.pathname.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);F=F[1];H=E.protocol+"//"+E.host+F+"/"+A.url;H=H.replace(/\&noheader=1/gi,"")}var G=new Ext.Window({width:300,plain:true,shadow:false,items:[new Ext.form.TextField({value:H,width:287})]});G.show()}},{text:"Delete",handler:function(){Ext.Msg.show({title:"Confirm Delete?",icon:Ext.MessageBox.QUESTION,msg:"Are you sure you want to delete this report?",buttons:Ext.Msg.YESNO,fn:function(E,F){if(E=="yes"){B.submit({url:"tr_query.cgi",params:{action:"delete_query",query_name:A.title},success:function(){Ext.getCmp("reports_grid").store.load();A.ownerCt.remove(A,true)},failure:testopiaError})}}})}}]});D.stopEvent();this.menu.showAt(D.getXY())}},{id:"close",handler:function(C,B,A){A.ownerCt.remove(A,true)}}];Testopia.Tags={};Testopia.Tags.renderer=function(C,H,G,A,D,F,E,B){return'
'+C+"
"};Testopia.Tags.list=function(D,E,A){var B={title:"Tag Results: "+A,closable:true,id:A+"search"+E,autoScroll:true};var C={product_id:E,tags:A};var F;if(D=="case"){F=new CaseGrid(C,B)}else{if(D=="plan"){F=new PlanGrid(C,B)}else{if(D=="run"){F=new RunGrid(C,B)}}}Ext.getCmp("object_panel").add(F);Ext.getCmp("object_panel").activate(A+"search"+E)};TestopiaObjectTags=function(D,A){this.orig_id=A;this.obj_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:D},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var C=this.store;this.remove=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"removetag",type:D,id:this.obj_id,tag:getSelectedObjects(Ext.getCmp(D+"tagsgrid"),"tag_name")},success:function(){C.reload()},failure:testopiaError})};this.add=function(){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:"addtag",type:D,id:this.obj_id,tag:Ext.getCmp(D+"tag_lookup").getRawValue()},success:function(){C.reload()},failure:testopiaError})};this.columns=[{dataIndex:"tag_id",hidden:true,hideable:false},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true,hideable:false},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case"],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run"],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,hidden:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan"],true)}];var B=new Ext.Button({id:"tag_add_btn",icon:"testopia/img/add.png",iconCls:"img_button_16x",handler:this.add.createDelegate(this)});var E=new Ext.Button({icon:"testopia/img/delete.png",iconCls:"img_button_16x",handler:this.remove.createDelegate(this)});TestopiaObjectTags.superclass.constructor.call(this,{title:"Tags",split:true,region:"east",layout:"fit",width:200,autoExpandColumn:"tag_name",collapsible:true,id:D+"tagsgrid",loadMask:{msg:"Loading "+D+" tags..."},autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true},tbar:[new TagLookup({id:D+"tag_lookup"}),B,E]});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaObjectTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Remove Selected Tags",icon:"testopia/img/delete.png",iconCls:"img_button_16x",obj_id:this.obj_id,handler:this.remove},{text:"Refresh List",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){B.store.reload()}}]})}C.stopEvent();if(B.getSelectionModel().getCount()<1){B.getSelectionModel().selectRow(A)}this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()||this.orig_id!=this.obj_id){this.store.load({params:{id:this.obj_id}})}}});TestopiaProductTags=function(F,C,A){var E;this.product_id=A;this.store=new Ext.data.JsonStore({url:"tr_tags.cgi",baseParams:{action:"gettags",type:C},root:"tags",id:"tag_id",fields:[{name:"tag_id",mapping:"tag_id"},{name:"tag_name",mapping:"tag_name"},{name:"run_count",mapping:"run_count"},{name:"case_count",mapping:"case_count"},{name:"plan_count",mapping:"plan_count"}]});var D=this.store;this.columns=[{header:"ID",dataIndex:"tag_id",hidden:true},{header:"Name",width:150,dataIndex:"tag_name",id:"tag_name",sortable:true},{header:"Cases",width:35,dataIndex:"case_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["case",A],true)},{header:"Runs",width:35,dataIndex:"run_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["run",A],true)},{header:"Plans",width:35,dataIndex:"plan_count",sortable:true,renderer:Testopia.Tags.renderer.createDelegate(this,["plan",A],true)}];var B=new Ext.form.TextField({allowBlank:true,id:"rungrid-filter",selectOnFocus:true});TestopiaProductTags.superclass.constructor.call(this,{title:F,id:C+"tags",loadMask:{msg:"Loading "+F+" ..."},autoExpandColumn:"tag_name",autoScroll:true,sm:new Ext.grid.RowSelectionModel({singleSelect:false}),viewConfig:{forceFit:true}});this.on("rowcontextmenu",this.onContextClick,this);this.on("activate",this.onActivate,this)};Ext.extend(TestopiaProductTags,Ext.grid.GridPanel,{onContextClick:function(B,A,C){if(!this.menu){this.menu=new Ext.menu.Menu({id:"tags-ctx-menu",items:[{text:"Refresh",icon:"testopia/img/refresh.png",iconCls:"img_button_16x",handler:function(){ds.reload()}}]})}C.stopEvent();this.menu.showAt(C.getXY())},onActivate:function(A){if(!this.store.getCount()){this.store.load({params:{product_id:this.product_id}})}}});TagsUpdate=function(B,A){function C(H,G,E){var F=new Ext.form.BasicForm("testopia_helper_frm",{});F.submit({url:"tr_tags.cgi",params:{action:H,tag:G,type:B,id:getSelectedObjects(E,B+"_id")},success:function(){},failure:testopiaError})}var D=new Ext.Window({title:"Add or Remove Tags",id:"tags_edit_win",layout:"fit",split:true,plain:true,shadow:false,width:350,height:150,items:[new Ext.FormPanel({labelWidth:"40",bodyStyle:"padding: 5px",items:[new TagLookup({fieldLabel:"Tags"})]})],buttons:[{text:"Add Tag",handler:function(){C("addtag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Remove Tag",handler:function(){C("removetag",Ext.getCmp("tag_lookup").getRawValue(),A);D.close()}},{text:"Close",handler:function(){D.close()}}]});D.show()}; \ No newline at end of file From 49810ec25fd33ae33eb8e9683f72ea9183bc6059 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 18:35:00 +0000 Subject: [PATCH 09/13] Bug 492952 - Adding attachment to test case fails as no mime type can be given --- Bugzilla/Testopia/Attachment.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/Bugzilla/Testopia/Attachment.pm b/Bugzilla/Testopia/Attachment.pm index 889d68a..98a5c01 100644 --- a/Bugzilla/Testopia/Attachment.pm +++ b/Bugzilla/Testopia/Attachment.pm @@ -166,6 +166,7 @@ sub create { $field_values->{contents} = _validate_data($field_values->{contents}); $field_values->{creation_ts} = Bugzilla::Testopia::Util::get_time_stamp(); + $field_values->{mime_type} ||= 'application/octet-stream'; my $contents = $field_values->{contents}; my $case_id = $field_values->{case_id}; From ee309af4c87e64f028223e0ab6afca2c957a019f Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 19:06:47 +0000 Subject: [PATCH 10/13] Need a better message as to why you can't delete things. --- .../user-error.html.tmpl/errors/tr-user-error.html.tmpl | 4 ++++ tr_list_caseruns.cgi | 2 +- tr_list_cases.cgi | 2 +- tr_list_runs.cgi | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl b/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl index 44edfed..6946386 100644 --- a/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl +++ b/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl @@ -166,6 +166,10 @@

The id's of those that failed are: [% list FILTER none %]

+ [% ELSIF error == "testopia-delete-failed" %] + [% title = "One or more of your test $object failed to be deleted" %] + Deleting one or more of your [% object %]s failed. The most likely cause is that you don't have permissions + to delete them. Your administrator must also have the "allow-test-deletion" set to "On". [% ELSIF error == "testopia-unknown-tag" %] The tag "[% name FILTER html %]" does not exist. [% ELSIF error == "testopia-regexp-too-inclusive" %] diff --git a/tr_list_caseruns.cgi b/tr_list_caseruns.cgi index 200743f..d58f580 100755 --- a/tr_list_caseruns.cgi +++ b/tr_list_caseruns.cgi @@ -128,7 +128,7 @@ elsif ($action eq 'delete'){ $case->obliterate($cgi->param('single')); } - ThrowUserError('testopia-update-failed', {'object' => 'case-run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + ThrowUserError('testopia-delete-failed', {'object' => 'case-run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); print "{'success': true}"; } diff --git a/tr_list_cases.cgi b/tr_list_cases.cgi index 1a090bd..4480c14 100755 --- a/tr_list_cases.cgi +++ b/tr_list_cases.cgi @@ -247,7 +247,7 @@ elsif ($action eq 'delete'){ $case->obliterate; } - ThrowUserError('testopia-update-failed', {'object' => 'case', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + ThrowUserError('testopia-delete-failed', {'object' => 'case', 'list' => join(',',@uneditable)}) if (scalar @uneditable); print "{'success': true}"; } diff --git a/tr_list_runs.cgi b/tr_list_runs.cgi index 9e1c9a7..75bc522 100755 --- a/tr_list_runs.cgi +++ b/tr_list_runs.cgi @@ -183,7 +183,7 @@ elsif ($action eq 'delete'){ $run->obliterate; } - ThrowUserError('testopia-update-failed', {'object' => 'run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); + ThrowUserError('testopia-delete-failed', {'object' => 'run', 'list' => join(',',@uneditable)}) if (scalar @uneditable); print "{'success': true}"; } From 7b522d3a1f7bd2a201c30f3efb09aa6399310bcd Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 19:14:16 +0000 Subject: [PATCH 11/13] Bug 508013 - Need a way to convert Testrunner 0.7 data to Testopia --- testopia/contrib/tesrunner2testopia.txt | 158 ++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 testopia/contrib/tesrunner2testopia.txt diff --git a/testopia/contrib/tesrunner2testopia.txt b/testopia/contrib/tesrunner2testopia.txt new file mode 100644 index 0000000..666d63f --- /dev/null +++ b/testopia/contrib/tesrunner2testopia.txt @@ -0,0 +1,158 @@ +Testrunner to Testopia installation guide by sneharaj.rajendran + +1, Upgrade the Bugzilla first before start the migration. + +2, Make sure that all the table related to test runner is dropped. + +Drop table test_case_attachments ; drop table test_case_groups; drop table test_case_tags; drop table test_cases; drop table test_cases_bugs; drop table test_cases_log; drop table test_cases_log_testers; drop table test_cases_texts; drop table test_plan_attachments; drop table test_plan_cases_tags; drop table test_plan_types; drop table test_plans; drop table test_plans_texts; drop table test_plans_watchers; drop table test_runs; drop table test_runs_testers; drop table testers; drop table testers_components; + +3, Apply the corresponding patch before the installation. + +Patch -p0 -i Testopia/ patch-3.0 + +4, To install the Testopia run the following command. + +Perl checksetup.pl + +The above command will create the table structure for the Testopia . + +Test Runner to Testopia migration + +1, Let start the database porting from test runner to testopia. + +Create two database base + +Eg: create database bugdump ; create database newdb; + +Note: newdb will be used for port the data from bugsdump database both will be temporary database used for migration. + +Note: restore the old bugzilla data in to one database + +2, Login to mysql. + +Mysql -u root -p bugsdump < "" + +3, login to new bugzilla database and ran the following commands. + +Use bugs; + +insert into test_case_status (case_status_id,name) values (3,'Disabled'); + +insert into test_case_status (case_status_id,name) values (1,'proposed'); + +insert into test_case_status (case_status_id,name) values (2,'Confirmed'); + + + +insert into test_builds (build_id,product_id,name) values (1,1,'Dummy data build'); + + insert into test_environments (environment_id,product_id,name) values (1,1,'Dummy data environment'); + +insert into test_case_run_status values (1,'idle'); + +insert into test_case_run_status values (2,'passed'); + +insert into test_case_run_status values (3,'failed'); + +4, Following sqlquerys will dump the data into newdb database + +use bugsdump; + +create table newdb.new_test_case_categories select distinct g.group_id as category_id, test_plans.product_id, concat(g.name, "_",g.group_id),g.description from test_cases, test_case_groups as g , test_plans where test_cases.group_id = g.group_id and test_plans.plan_id = test_cases.plan_id; + +create table newdb.new_test_case_tags select test_case_tags.tag_id,test_case_tags.case_id, test_cases.userid from test_case_tags, test_cases where test_cases.case_id = test_case_tags.case_id; + + + create table newdb.new_test_case_plans select plan_id, case_id from test_cases; + + create table newdb.new_test_case_components select case_id, component_id from test_cases; + + create table newdb.new_test_case_bugs select bug_id,case_log_id as case_run_id, case_id from test_cases_bugs; + + create table newdb.new_test_case_runs select id as case_run_id ,test_run_id as run_id, case_id,runningby as assignee,testedby,if(status=1,1,if(status=2,2,3)) as case_run_status_id, test_case_version,'1' as build_id, runningon as running_date, close_date as close_date, notes,isprivate as iscurrent, seq as sortkey, NULL as environment_id from test_cases_log; + + create table newdb.new_test_case_texts select case_id, case_version as case_text_version, '1' as who , creation_ts, action,effect,summary as setup, req_id as breakdown from test_cases_texts; + + create table newdb.new_test_plan_types select type_id,name, '' as description from test_plan_types; + + create table newdb.new_test_plan_tags select tag_id,plan_id, '1' as userid from test_plan_cases_tags; + + create table newdb.new_test_plans select t.plan_id,t.product_id,t.editor as author_id,t.type as type_id ,t.product_version as default_product_version,t.name,t.creation_date,if((t.active= 'T'), 1, 0) as isactive from test_plans t, products p where p.id= t.product_id; + + + create table newdb.new_test_plan_texts select plan_id, plan_version as plan_text_version, '1' as who, creation_ts, plan_text from test_plans_texts; + + create table newdb.new_test_runs select test_run_id as run_id, plan_id, NULL as environment_id , product_version, NULL as build_id, plan_version as plan_text_version, manager as manager_id, NULL as default_tester_id, start_date, stop_date, summary, notes from test_runs; + +create table newdb.new_test_cases select test_cases.case_id, if(status=1,1,if(status=2,2,3)) as case_status_id, group_id as category_id, 0 as priority_id,userid as author_id, 0 as default_tester_id, creation_date, 0 as estimated_time, if(strcmp(run_mode,'manual'),1,0) as isautomated,seq as sortkey, script, arguments, test_cases_texts.summary ,'' as requirement, '' as alias from test_cases left join test_cases_texts on (test_cases.case_id=test_cases_texts.case_id) group by test_cases_texts.case_id; + +5, Take the dump of each table by table from newdb +6, logout from bugzilla database + +7, create a folder name dump inside buzilla installation folder and execute the following command one by one to take the back of table created under newdb database + +mysqldump -u root -p newdb new_test_case_categories -t > new_test_case_categories + +mysqldump -u root -p newdb new_test_case_components -t > new_test_case_components + +mysqldump -u root -p newdb new_test_case_plans -t > new_test_case_plans + +mysqldump -u root -p newdb new_test_case_runs -t > new_test_case_runs + +mysqldump -u root -p newdb new_test_case_tags -t > new_test_case_tags + +mysqldump -u root -p newdb new_test_case_texts -t > new_test_case_texts + +mysqldump -u root -p newdb new_test_cases -t > new_test_cases + +mysqldump -u root -p newdb new_test_plan_tags -t > new_test_plan_tags + +mysqldump -u root -p newdb new_test_plan_texts -t > new_test_plan_texts + +mysqldump -u root -p newdb new_test_plan_types -t > new_test_plan_types + +mysqldump -u root -p newdb new_test_plans -t > new_test_plans + +mysqldump -u root -p newdb new_test_runs -t > new_test_runs + +mysqldump -u root -p newdb new_test_case_bugs -t > new_test_case_bugs + +8, Open each file and replace the word + +:1,$s/new_//g + +9, Restore the data into bugs one by one + +Mysql -u root -p bugs < new_test_case_components + +Mysql -u root -p bugs < new_test_case_plans + +Mysql -u root -p bugs < new_test_case_runs + +Mysql -u root -p bugs < new_test_case_tags + +Mysql -u root -p bugs < new_test_case_texts + +Mysql -u root -p bugs < new_test_cases + +Mysql -u root -p bugs < new_test_plan_tags + +Mysql -u root -p bugs < new_test_plan_texts + +Mysql -u root -p bugs < new_test_plan_types + +Mysql -u root -p bugs < new_test_plans + +Mysql -u root -p bugs < new_test_runs + +Mysql -u root -p bugs < new_test_case_categories + +10, Login to mysql database + +Mysql -u root -p + +Use bugs; + +update test_case_runs set iscurrent =1; + +11, Bugzilla and testopia is ready to test. \ No newline at end of file From f93ecbca686a7874baae3df7cd073c84ca2a2d97 Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Wed, 19 Aug 2009 19:40:58 +0000 Subject: [PATCH 12/13] Bug 508815 - TestCase.unlink_plan(): can not unlink TestCase from last TestPlan (delete) --- Bugzilla/Testopia/TestCase.pm | 32 ++++++++++++++++---------------- tr_list_cases.cgi | 2 +- tr_process_case.cgi | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Bugzilla/Testopia/TestCase.pm b/Bugzilla/Testopia/TestCase.pm index 7af57b8..f4d52ef 100644 --- a/Bugzilla/Testopia/TestCase.pm +++ b/Bugzilla/Testopia/TestCase.pm @@ -1268,24 +1268,24 @@ sub unlink_plan { my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); if (scalar @{$self->plans} == 1){ - return 0; #Return failure + $self->obliterate; } - - $dbh->bz_start_transaction(); - - foreach my $run (@{$plan->test_runs}){ - $dbh->do("DELETE FROM test_case_runs - WHERE case_id = ? - AND run_id = ?", undef, $self->id, $run->id); + else { + $dbh->bz_start_transaction(); + + foreach my $run (@{$plan->test_runs}){ + $dbh->do("DELETE FROM test_case_runs + WHERE case_id = ? + AND run_id = ?", undef, $self->id, $run->id); + } + + $dbh->do("DELETE FROM test_case_plans + WHERE plan_id = ? + AND case_id = ?", + undef, $plan_id, $self->{'case_id'}); + + $dbh->bz_commit_transaction(); } - - $dbh->do("DELETE FROM test_case_plans - WHERE plan_id = ? - AND case_id = ?", - undef, $plan_id, $self->{'case_id'}); - - $dbh->bz_commit_transaction(); - # Update the plans array. delete $self->{'plans'}; diff --git a/tr_list_cases.cgi b/tr_list_cases.cgi index 4480c14..0e8afd8 100755 --- a/tr_list_cases.cgi +++ b/tr_list_cases.cgi @@ -264,7 +264,7 @@ elsif ($action eq 'unlink'){ } else { ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->can_unlink_plan($plan_id)); - ThrowUserError('testopia-case-unlink-failure') unless $case->unlink_plan($plan_id); + $case->unlink_plan($plan_id); } } print "{'success': true}"; diff --git a/tr_process_case.cgi b/tr_process_case.cgi index 1ddd755..d9e49f9 100755 --- a/tr_process_case.cgi +++ b/tr_process_case.cgi @@ -118,7 +118,7 @@ elsif ($action eq 'unlink'){ my $plan_id = $cgi->param('plan_id'); validate_test_id($plan_id, 'plan'); ThrowUserError("testopia-read-only", {'object' => 'case'}) unless ($case->can_unlink_plan($plan_id)); - ThrowUserError('testopia-case-unlink-failure') unless $case->unlink_plan($plan_id); + $case->unlink_plan($plan_id); print "{'success': true}"; } From 7981ba583eb9df3f201d35defe2fd225ab99cd9b Mon Sep 17 00:00:00 2001 From: Gregary Hendricks Date: Fri, 21 Aug 2009 16:41:45 +0000 Subject: [PATCH 13/13] Bug 477264 - Server error 'Foreign key constraint fails...' when adding any attribute into an environment --- extensions/testopia/code/db_schema-abstract_schema.pl | 5 ----- extensions/testopia/code/install-update_db.pl | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/extensions/testopia/code/db_schema-abstract_schema.pl b/extensions/testopia/code/db_schema-abstract_schema.pl index e89cdcd..b9c2ac1 100644 --- a/extensions/testopia/code/db_schema-abstract_schema.pl +++ b/extensions/testopia/code/db_schema-abstract_schema.pl @@ -972,11 +972,6 @@ property_id => { TYPE => 'INT4', NOTNULL => 1, - REFERENCES => { - TABLE => 'test_environment_property', - COLUMN => 'property_id', - DELETE => 'CASCADE' - } }, element_id => { TYPE => 'INT4', diff --git a/extensions/testopia/code/install-update_db.pl b/extensions/testopia/code/install-update_db.pl index c0c6226..89186d0 100644 --- a/extensions/testopia/code/install-update_db.pl +++ b/extensions/testopia/code/install-update_db.pl @@ -63,7 +63,8 @@ sub testopiaUpdateDB { $dbh->bz_drop_column('test_plans', 'editor_id'); $dbh->bz_drop_fk('test_cases', 'priority_id') if $dbh->bz_column_info('test_cases','priority_id')->{REFERENCES}->{DELETE} eq 'CASCADE'; - + $dbh->bz_drop_fk('test_environment_map', 'property_id'); + $dbh->bz_add_column('test_case_bugs', 'case_id', {TYPE => 'INT4'}); $dbh->bz_add_column('test_case_runs', 'environment_id', {TYPE => 'INT4', NOTNULL => 1}, 0); $dbh->bz_add_column('test_case_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0);