← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/test-domain into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/test-domain into lp:launchpad.

Commit message:
Move development sites from .dev to .test.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/test-domain/+merge/367784

We need this now that Google has bought .dev and added it to HSTS preload lists.

This commit is the result of the following commands:

  bzr grep -l '\.dev\b' | \
    xargs sed -Ei 's/(launchpad|l\.\.\.d|librarian|login1|login2|mail-archive|testopenid|\.\.)\.dev\b/\1\.test/g'
  sed -i '/^launchpadlib==/s/==.*/==1.10.7/' constraints.txt

If you don't want to re-run "make install", then run the following in the relevant container:

  sed -Ei 's/(launchpad|testopenid)\.dev\b/\1\.test/g' /etc/hosts /etc/apache2/sites-available/local-launchpad.conf
  mv /var/tmp/bazaar.launchpad.dev /var/tmp/bazaar.launchpad.test

If you don't want to re-run "make schema", then run the following in psql for each relevant database (by default, launchpad_empty, launchpad_dev_template, launchpad_dev, launchpad_ftest_template, and
  launchpad_ftest_playground):

  UPDATE logintoken SET redirection_url = regexp_replace(redirection_url, '\.dev', '.test');
  UPDATE publisherconfig SET base_url = regexp_replace(base_url, '\.dev', '.test');
-- 
The attached diff has been truncated due to its size.
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/test-domain into lp:launchpad.
=== modified file 'Makefile'
--- Makefile	2019-04-16 14:30:40 +0000
+++ Makefile	2019-05-22 15:20:07 +0000
@@ -45,9 +45,9 @@
 
 MINS_TO_SHUTDOWN=15
 
-CODEHOSTING_ROOT=/var/tmp/bazaar.launchpad.dev
+CODEHOSTING_ROOT=/var/tmp/bazaar.launchpad.test
 
-CONVOY_ROOT?=/srv/launchpad.dev/convoy
+CONVOY_ROOT?=/srv/launchpad.test/convoy
 
 VERSION_INFO = version-info.py
 
@@ -155,7 +155,7 @@
 endif
 
 inplace: build logs clean_logs codehosting-dir
-	if [ -d /srv/launchpad.dev ]; then \
+	if [ -d /srv/launchpad.test ]; then \
 		ln -sfn $(WD)/build/js $(CONVOY_ROOT); \
 	fi
 
@@ -474,9 +474,9 @@
 		-e 's,%LISTEN_ADDRESS%,$(LISTEN_ADDRESS),' \
 		configs/development/local-launchpad-apache > \
 		/etc/apache2/sites-available/$$base
-	if [ ! -d /srv/launchpad.dev ]; then \
-		mkdir /srv/launchpad.dev; \
-		chown $(SUDO_UID):$(SUDO_GID) /srv/launchpad.dev; \
+	if [ ! -d /srv/launchpad.test ]; then \
+		mkdir /srv/launchpad.test; \
+		chown $(SUDO_UID):$(SUDO_GID) /srv/launchpad.test; \
 	fi
 
 enable-apache-launchpad: copy-apache-config copy-certificates

=== modified file 'configs/README.txt'
--- configs/README.txt	2014-01-30 15:04:06 +0000
+++ configs/README.txt	2019-05-22 15:20:07 +0000
@@ -111,10 +111,10 @@
 
     >>> test_data = ("""
     ...     [answertracker]
-    ...     email_domain: answers.launchpad.dev""")
+    ...     email_domain: answers.launchpad.test""")
     >>> config.push('test_data', test_data)
     >>> config.answertracker.email_domain
-    'answers.launchpad.dev'
+    'answers.launchpad.test'
 
 And tests can remove the data with pop() when they are done to restore
 the config.

=== modified file 'configs/development/launchpad-lazr.conf'
--- configs/development/launchpad-lazr.conf	2019-04-27 08:35:51 +0000
+++ configs/development/launchpad-lazr.conf	2019-05-22 15:20:07 +0000
@@ -26,32 +26,32 @@
 bugzilla-3.4.example.com.password: test
 
 [codebrowse]
-cachepath: /var/tmp/bazaar.launchpad.dev/cache
-log_folder: /var/tmp/bazaar.launchpad.dev/logs
-launchpad_root: https://code.launchpad.dev/
+cachepath: /var/tmp/bazaar.launchpad.test/cache
+log_folder: /var/tmp/bazaar.launchpad.test/logs
+launchpad_root: https://code.launchpad.test/
 secret_path: configs/development/codebrowse-secret
 
 [codehosting]
 launch: True
-authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver
-codehosting_endpoint: http://xmlrpc-private.launchpad.dev:8087/codehosting
-supermirror_root: http://bazaar.launchpad.dev/
-secure_codebrowse_root: https://bazaar.launchpad.dev/
-internal_branch_by_id_root: http://bazaar-internal.launchpad.dev/
+authentication_endpoint: http://xmlrpc-private.launchpad.test:8087/authserver
+codehosting_endpoint: http://xmlrpc-private.launchpad.test:8087/codehosting
+supermirror_root: http://bazaar.launchpad.test/
+secure_codebrowse_root: https://bazaar.launchpad.test/
+internal_branch_by_id_root: http://bazaar-internal.launchpad.test/
 internal_codebrowse_root: http://localhost:8080/
-rewrite_script_log_file: /var/tmp/bazaar.launchpad.dev/rewrite.log
+rewrite_script_log_file: /var/tmp/bazaar.launchpad.test/rewrite.log
 host_key_pair_path: lib/lp/codehosting/sshserver/tests/keys
 port: tcp:5022:interface=127.0.0.88
 bzr_lp_prefix: lp://dev/
 lp_url_hosts: dev
-access_log: /var/tmp/bazaar.launchpad.dev/codehosting-access.log
+access_log: /var/tmp/bazaar.launchpad.test/codehosting-access.log
 blacklisted_hostnames:
 use_forking_daemon: True
-internal_bzr_api_endpoint: http://bazaar.launchpad.dev:8090/
-internal_git_api_endpoint: http://git.launchpad.dev:19417/
-git_browse_root: https://git.launchpad.dev/
-git_anon_root: git://git.launchpad.dev/
-git_ssh_root: git+ssh://git.launchpad.dev/
+internal_bzr_api_endpoint: http://bazaar.launchpad.test:8090/
+internal_git_api_endpoint: http://git.launchpad.test:19417/
+git_browse_root: https://git.launchpad.test/
+git_anon_root: git://git.launchpad.test/
+git_ssh_root: git+ssh://git.launchpad.test/
 
 [codeimport]
 bazaar_branch_store: file:///tmp/bazaar-branches
@@ -61,7 +61,7 @@
 forced_hostname: bazaar-importer
 
 [commercial]
-voucher_proxy_url: http://launchpad.dev
+voucher_proxy_url: http://launchpad.test
 voucher_proxy_port: 2323
 voucher_proxy_timeout: 60000
 purchase_subscription_url: http://ubuntu.recycledmania.com/product_info.php?products_id=227
@@ -81,7 +81,7 @@
 
 [bing]
 # Development and the testrunner should use the stub service by default.
-site: http://launchpad.dev:8093/bingcustomsearch/v7.0/search
+site: http://launchpad.test:8093/bingcustomsearch/v7.0/search
 subscription_key: abcdef01234567890abcdef012345678
 custom_config_id: 1234567890
 
@@ -89,15 +89,15 @@
 launch: True
 
 [gpghandler]
-host: keyserver.launchpad.dev
-public_host: keyserver.launchpad.dev
+host: keyserver.launchpad.test
+public_host: keyserver.launchpad.test
 public_https: False
 
 [launchpad]
 enable_test_openid_provider: True
-openid_canonical_root: https://testopenid.dev/
-openid_provider_root: https://testopenid.dev/
-code_domain: code.launchpad.dev
+openid_canonical_root: https://testopenid.test/
+openid_provider_root: https://testopenid.test/
+code_domain: code.launchpad.test
 default_batch_size: 5
 max_attachment_size: 2097152
 branchlisting_batch_size: 6
@@ -108,21 +108,21 @@
 max_bug_feed_cache_minutes: 30
 bzr_imports_root_url: file:///tmp/bazaar-branches
 geoip_database: /usr/share/GeoIP/GeoLiteCity.dat
-feature_flags_endpoint: http://xmlrpc-private.launchpad.dev:8087/featureflags/
+feature_flags_endpoint: http://xmlrpc-private.launchpad.test:8087/featureflags/
 
 [launchpad_session]
 cookie: launchpad_dev
 database: dbname=session_dev
 
 [librarian]
-download_url: http://launchpad.dev:58080/
+download_url: http://launchpad.test:58080/
 upload_port: 58090
 download_port: 58080
 restricted_upload_port: 58095
 restricted_download_port: 58085
-restricted_download_url: http://launchpad.dev:58085/
+restricted_download_url: http://launchpad.test:58085/
 use_https: False
-authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver
+authentication_endpoint: http://xmlrpc-private.launchpad.test:8087/authserver
 
 [librarian_server]
 root: /var/tmp/fatsam
@@ -147,12 +147,12 @@
 build_var_dir: /var/tmp/mailman
 xmlrpc_runner_sleep: 5
 smtp: localhost:9025
-list_help_header: http://help.launchpad.dev/ListHelp
-archive_address: archive@xxxxxxxxxxxxxxxx
-list_owner_header_template: http://launchpad.dev/~$team_name
-archive_url_template: http://lists.launchpad.dev/$team_name
-list_subscription_headers: http://launchpad.dev/~$team_name
-build_host_name: lists.launchpad.dev
+list_help_header: http://help.launchpad.test/ListHelp
+archive_address: archive@xxxxxxxxxxxxxxxxx
+list_owner_header_template: http://launchpad.test/~$team_name
+archive_url_template: http://lists.launchpad.test/$team_name
+list_subscription_headers: http://launchpad.test/~$team_name
+build_host_name: lists.launchpad.test
 # Crank these way down so they're easier to test.
 soft_max_size: 40000
 hard_max_size: 1000000
@@ -170,11 +170,11 @@
 [personalpackagearchive]
 root: /var/tmp/ppa/
 private_root: /var/tmp/ppa
-base_url: http://ppa.launchpad.dev
-private_base_url: http://private-ppa.launchpad.dev
+base_url: http://ppa.launchpad.test
+private_base_url: http://private-ppa.launchpad.test
 
 [poppy]
-authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver
+authentication_endpoint: http://xmlrpc-private.launchpad.test:8087/authserver
 host_key_private=lib/lp/poppy/tests/poppy-sftp
 host_key_public=lib/lp/poppy/tests/poppy-sftp.pub
 port: tcp:5023
@@ -187,9 +187,9 @@
 virtual_host: /
 
 [snappy]
-builder_proxy_auth_api_admin_username: admin-launchpad.dev
-builder_proxy_auth_api_endpoint: http://snap-proxy.launchpad.dev:8080/tokens
-builder_proxy_host: snap-proxy.launchpad.dev
+builder_proxy_auth_api_admin_username: admin-launchpad.test
+builder_proxy_auth_api_endpoint: http://snap-proxy.launchpad.test:8080/tokens
+builder_proxy_host: snap-proxy.launchpad.test
 builder_proxy_port: 3128
 store_search_url: https://api.snapcraft.io/
 tools_source: deb http://ppa.launchpad.net/snappy-dev/snapcraft-daily/ubuntu %(series)s main
@@ -211,39 +211,39 @@
 use_https: True
 
 [vhost.mainsite]
-hostname: launchpad.dev
+hostname: launchpad.test
 althostnames: localhost
 openid_delegate_profile: True
 
 [vhost.api]
-hostname: api.launchpad.dev
+hostname: api.launchpad.test
 
 [vhost.blueprints]
-hostname: blueprints.launchpad.dev
+hostname: blueprints.launchpad.test
 
 [vhost.code]
-hostname: code.launchpad.dev
+hostname: code.launchpad.test
 
 [vhost.translations]
-hostname: translations.launchpad.dev
+hostname: translations.launchpad.test
 
 [vhost.bugs]
-hostname: bugs.launchpad.dev
+hostname: bugs.launchpad.test
 
 [vhost.answers]
-hostname: answers.launchpad.dev
+hostname: answers.launchpad.test
 
 [vhost.testopenid]
-hostname: testopenid.dev
+hostname: testopenid.test
 
 [vhost.xmlrpc]
-hostname: xmlrpc.launchpad.dev
+hostname: xmlrpc.launchpad.test
 
 [vhost.xmlrpc_private]
-hostname: xmlrpc-private.launchpad.dev
+hostname: xmlrpc-private.launchpad.test
 
 [vhost.feeds]
-hostname: feeds.launchpad.dev
+hostname: feeds.launchpad.test
 
 [immediate_mail]
 # XXX sinzui 2008-03-26:

=== modified file 'configs/development/local-launchpad-apache'
--- configs/development/local-launchpad-apache	2019-04-16 14:30:40 +0000
+++ configs/development/local-launchpad-apache	2019-05-22 15:20:07 +0000
@@ -13,20 +13,20 @@
 # These have to be first so non-Host/non-SNI requests don't hit bazaar etc.
 # But they can't have a ServerAlias wildcard or they'll shadow bazaar.
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName launchpad.dev
-  ServerAlias answers.launchpad.dev api.launchpad.dev blueprints.launchpad.dev
-  ServerAlias bugs.launchpad.dev code.launchpad.dev feeds.launchpad.dev
-  ServerAlias translations.launchpad.dev xmlrpc.launchpad.dev testopenid.dev
+  ServerName launchpad.test
+  ServerAlias answers.launchpad.test api.launchpad.test blueprints.launchpad.test
+  ServerAlias bugs.launchpad.test code.launchpad.test feeds.launchpad.test
+  ServerAlias translations.launchpad.test xmlrpc.launchpad.test testopenid.test
 
   RewriteEngine On
   RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:443>
-  ServerName launchpad.dev
-  ServerAlias answers.launchpad.dev api.launchpad.dev blueprints.launchpad.dev
-  ServerAlias bugs.launchpad.dev code.launchpad.dev feeds.launchpad.dev
-  ServerAlias translations.launchpad.dev xmlrpc.launchpad.dev testopenid.dev
+  ServerName launchpad.test
+  ServerAlias answers.launchpad.test api.launchpad.test blueprints.launchpad.test
+  ServerAlias bugs.launchpad.test code.launchpad.test feeds.launchpad.test
+  ServerAlias translations.launchpad.test xmlrpc.launchpad.test testopenid.test
   SSLEngine On
   SSLCertificateFile /etc/apache2/ssl/launchpad.crt
   SSLCertificateKeyFile /etc/apache2/ssl/launchpad.key
@@ -52,7 +52,7 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName bazaar.launchpad.dev
+  ServerName bazaar.launchpad.test
   LogLevel debug
 
   ProxyRequests off
@@ -62,12 +62,12 @@
   RewriteMap branch-rewrite prg:%BRANCH_REWRITE%
   RewriteMap escape int:escape
 
-  RewriteRule ^/$ http://launchpad.dev [L]
+  RewriteRule ^/$ http://launchpad.test [L]
 
   RewriteRule ^(/.*)$ ${branch-rewrite:${escape:$1}} [L,P,NE]
 
-  DocumentRoot /var/tmp/bazaar.launchpad.dev/static/
-  <Directory /var/tmp/bazaar.launchpad.dev/static/>
+  DocumentRoot /var/tmp/bazaar.launchpad.test/static/
+  <Directory /var/tmp/bazaar.launchpad.test/static/>
     Options SymLinksIfOwnerMatch
     AllowOverride None
     Options Indexes
@@ -79,7 +79,7 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:443>
-  ServerName bazaar.launchpad.dev
+  ServerName bazaar.launchpad.test
   LogLevel debug
 
   SSLEngine On
@@ -94,7 +94,7 @@
 
   RequestHeader add X-Forwarded-Scheme https
 
-  RewriteRule ^/$ http://launchpad.dev [L]
+  RewriteRule ^/$ http://launchpad.test [L]
   RewriteRule ^/(.*)$ http://localhost:8080/$1 [L,P]
 
   <Location />
@@ -103,11 +103,11 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName bazaar-internal.launchpad.dev
+  ServerName bazaar-internal.launchpad.test
   LogLevel debug
 
-  DocumentRoot /var/tmp/bazaar.launchpad.dev/mirrors
-  <Directory /var/tmp/bazaar.launchpad.dev/mirrors/>
+  DocumentRoot /var/tmp/bazaar.launchpad.test/mirrors
+  <Directory /var/tmp/bazaar.launchpad.test/mirrors/>
     <IfVersion >= 2.4>
       Require ip 127.0.0.0/255.0.0.0
     </IfVersion>
@@ -123,7 +123,7 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName lists.launchpad.dev
+  ServerName lists.launchpad.test
   Alias  /   /var/tmp/mailman/mhonarc/
   <Directory /var/tmp/mailman/mhonarc>
     AllowOverride None
@@ -132,8 +132,8 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName ppa.launchpad.dev
-  ServerAlias private-ppa.launchpad.dev
+  ServerName ppa.launchpad.test
+  ServerAlias private-ppa.launchpad.test
   LogLevel debug
 
   DocumentRoot /var/tmp/ppa
@@ -152,7 +152,7 @@
 </VirtualHost>
 
 <VirtualHost %LISTEN_ADDRESS%:80>
-  ServerName archive.launchpad.dev
+  ServerName archive.launchpad.test
   LogLevel debug
 
   DocumentRoot /var/tmp/archive

=== modified file 'configs/development/txpkgupload.yaml'
--- configs/development/txpkgupload.yaml	2015-01-13 16:02:40 +0000
+++ configs/development/txpkgupload.yaml	2019-05-22 15:20:07 +0000
@@ -13,7 +13,7 @@
 sftp:
   ## The URL of the XML-RPC endpoint that handles authentication of SSH
   ## users.
-  authentication_endpoint: "http://xmlrpc-private.launchpad.dev:8087/authserver";
+  authentication_endpoint: "http://xmlrpc-private.launchpad.test:8087/authserver";
   ## The absolute path to the private key used for the SFTP server.
   host_key_private: "configs/development/txpkgupload-sftp"
   ## The absolute path to the public key used for the SFTP server.

=== modified file 'configs/testrunner-appserver/launchpad-lazr.conf'
--- configs/testrunner-appserver/launchpad-lazr.conf	2018-11-01 18:03:06 +0000
+++ configs/testrunner-appserver/launchpad-lazr.conf	2019-05-22 15:20:07 +0000
@@ -12,7 +12,7 @@
 launch: False
 
 [launchpad]
-openid_provider_root: http://testopenid.dev:8085/
+openid_provider_root: http://testopenid.test:8085/
 internal_macaroon_secret_key: internal-dev-macaroon-secret
 
 [librarian_server]
@@ -24,37 +24,37 @@
 register_bounces_every: 1
 
 [vhost.mainsite]
-rooturl: http://launchpad.dev:8085/
+rooturl: http://launchpad.test:8085/
 
 [vhost.api]
-rooturl: http://api.launchpad.dev:8085/
+rooturl: http://api.launchpad.test:8085/
 
 [vhost.blueprints]
-rooturl: http://blueprints.launchpad.dev:8085/
+rooturl: http://blueprints.launchpad.test:8085/
 
 [vhost.code]
-rooturl: http://code.launchpad.dev:8085/
+rooturl: http://code.launchpad.test:8085/
 
 [vhost.translations]
-rooturl: http://translations.launchpad.dev:8085/
+rooturl: http://translations.launchpad.test:8085/
 
 [vhost.bugs]
-rooturl: http://bugs.launchpad.dev:8085/
+rooturl: http://bugs.launchpad.test:8085/
 
 [vhost.answers]
-rooturl: http://answers.launchpad.dev:8085/
+rooturl: http://answers.launchpad.test:8085/
 
 [vhost.testopenid]
-rooturl: http://testopenid.dev:8085/
+rooturl: http://testopenid.test:8085/
 
 [vhost.xmlrpc]
-rooturl: http://xmlrpc.launchpad.dev:8085/
+rooturl: http://xmlrpc.launchpad.test:8085/
 
 [vhost.xmlrpc_private]
-rooturl: http://xmlrpc-private.launchpad.dev:8085/
+rooturl: http://xmlrpc-private.launchpad.test:8085/
 
 [vhost.feeds]
-rooturl: http://feeds.launchpad.dev:8085/
+rooturl: http://feeds.launchpad.test:8085/
 
 [immediate_mail]
 # BarryWarsaw 04-Dec-2008: AppServerLayer tests should send email to the fake

=== modified file 'configs/testrunner/launchpad-lazr.conf'
--- configs/testrunner/launchpad-lazr.conf	2019-04-27 08:35:51 +0000
+++ configs/testrunner/launchpad-lazr.conf	2019-05-22 15:20:07 +0000
@@ -19,9 +19,9 @@
 [codehosting]
 bzr_lp_prefix: lp://dev/
 host_key_pair_path: lib/lp/codehosting/sshserver/tests/keys
-port: tcp:22222:interface=bazaar.launchpad.dev
+port: tcp:22222:interface=bazaar.launchpad.test
 access_log: /tmp/test-codehosting-access.log
-internal_branch_by_id_root: file:///var/tmp/bazaar.launchpad.dev/mirrors
+internal_branch_by_id_root: file:///var/tmp/bazaar.launchpad.test/mirrors
 
 [database]
 rw_main_master: dbname=launchpad_ftest
@@ -89,7 +89,7 @@
 root: /tmp/gina_test_archive
 
 [bing]
-site: http://launchpad.dev:8093/bingcustomsearch/v7.0/search
+site: http://launchpad.test:8093/bingcustomsearch/v7.0/search
 
 [gpghandler]
 upload_keys: True
@@ -105,10 +105,10 @@
 max_attachment_size: 1024
 geoip_database: /usr/share/GeoIP/GeoLiteCity.dat
 logparser_max_parsed_lines: 100000
-homepage_recent_posts_feed: http://launchpad.dev:8093/blog-feed
-openid_canonical_root: http://testopenid.dev/
-openid_provider_root: http://testopenid.dev/
-openid_alternate_provider_roots: http://login1.dev/, http://login2.dev/
+homepage_recent_posts_feed: http://launchpad.test:8093/blog-feed
+openid_canonical_root: http://testopenid.test/
+openid_provider_root: http://testopenid.test/
+openid_alternate_provider_roots: http://login1.test/, http://login2.test/
 
 [launchpad_session]
 cookie: launchpad_tests

=== modified file 'constraints.txt'
--- constraints.txt	2019-04-28 15:22:08 +0000
+++ constraints.txt	2019-05-22 15:20:07 +0000
@@ -276,7 +276,7 @@
 keyring==0.6.2
 kombu==4.4.0
 launchpad-buildd==159
-launchpadlib==1.10.5
+launchpadlib==1.10.7
 lazr.authentication==0.1.1
 lazr.batchnavigator==1.2.11
 lazr.config==2.2.1

=== modified file 'database/sampledata/README'
--- database/sampledata/README	2012-05-11 05:21:08 +0000
+++ database/sampledata/README	2019-05-22 15:20:07 +0000
@@ -2,9 +2,9 @@
 ===========
 
 There are two sorts of sample data. One used by developers when playing around
-in http://launchpad.dev and another used by our tests.
+in http://launchpad.test and another used by our tests.
 
-The sample data used by default in http://launchpad.dev is stored as a SQL
+The sample data used by default in http://launchpad.test is stored as a SQL
 file called current-dev.sql, in this same directory. That data is loaded into
 the launchpad_dev DB and you can change it as you like, as its main purpose is
 to aid developers experimenting all features of Launchpad.
@@ -12,7 +12,7 @@
 Sample data for tests is stored as a SQL file called current.sql, under this
 same directory. This data is loaded into the launchpad_ftest_template and
 launchpad_ftest_playground DBs. The former is used by our test runner and the
-latter can be accessed through http://launchpad.dev when you run the web app
+latter can be accessed through http://launchpad.test when you run the web app
 using the test-playground config (i.e. make LPCONFIG=test-playground run),
 so that you can see the world as one of our tests would.
 

=== modified file 'database/sampledata/current-dev.sql'
--- database/sampledata/current-dev.sql	2019-04-16 14:30:40 +0000
+++ database/sampledata/current-dev.sql	2019-05-22 15:20:07 +0000
@@ -4682,7 +4682,7 @@
 
 ALTER TABLE logintoken DISABLE TRIGGER ALL;
 
-INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (1, NULL, NULL, 'bac@xxxxxxxxxxxxx', '2008-06-27 14:48:38.308457', 3, '4d0a02130ab51372bb2114a9abe3ad8482810bdac58a4231c85c005c923cd57c', NULL, 'http://launchpad.dev', '2008-06-27 14:49:11.149508');
+INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (1, NULL, NULL, 'bac@xxxxxxxxxxxxx', '2008-06-27 14:48:38.308457', 3, '4d0a02130ab51372bb2114a9abe3ad8482810bdac58a4231c85c005c923cd57c', NULL, 'http://launchpad.test', '2008-06-27 14:49:11.149508');
 INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (2, 16, 'foo.bar@xxxxxxxxxxxxx', 'admin@xxxxxxxxxxxxx', '2008-08-05 11:59:42.448213', 4, 'd617585e5d8be1b4eba0c4d5f5058f0f761f6d2912d8c780b257d483ac3e5c8f', NULL, NULL, '2008-08-05 12:01:32.086327');
 
 
@@ -9186,8 +9186,8 @@
 
 ALTER TABLE publisherconfig DISABLE TRIGGER ALL;
 
-INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
-INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.test/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.test/', 'http://rebuild-test.internal/');
 
 
 ALTER TABLE publisherconfig ENABLE TRIGGER ALL;

=== modified file 'database/sampledata/current.sql'
--- database/sampledata/current.sql	2017-12-15 12:15:25 +0000
+++ database/sampledata/current.sql	2019-05-22 15:20:07 +0000
@@ -4596,7 +4596,7 @@
 
 ALTER TABLE logintoken DISABLE TRIGGER ALL;
 
-INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (1, NULL, NULL, 'bac@xxxxxxxxxxxxx', '2008-06-27 14:48:38.308457', 3, '4d0a02130ab51372bb2114a9abe3ad8482810bdac58a4231c85c005c923cd57c', NULL, 'http://launchpad.dev', '2008-06-27 14:49:11.149508');
+INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (1, NULL, NULL, 'bac@xxxxxxxxxxxxx', '2008-06-27 14:48:38.308457', 3, '4d0a02130ab51372bb2114a9abe3ad8482810bdac58a4231c85c005c923cd57c', NULL, 'http://launchpad.test', '2008-06-27 14:49:11.149508');
 INSERT INTO logintoken (id, requester, requesteremail, email, created, tokentype, token, fingerprint, redirection_url, date_consumed) VALUES (2, 16, 'foo.bar@xxxxxxxxxxxxx', 'admin@xxxxxxxxxxxxx', '2008-08-05 11:59:42.448213', 4, 'd617585e5d8be1b4eba0c4d5f5058f0f761f6d2912d8c780b257d483ac3e5c8f', NULL, NULL, '2008-08-05 12:01:32.086327');
 
 
@@ -9100,8 +9100,8 @@
 
 ALTER TABLE publisherconfig DISABLE TRIGGER ALL;
 
-INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
-INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.test/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.test/', 'http://rebuild-test.internal/');
 
 
 ALTER TABLE publisherconfig ENABLE TRIGGER ALL;

=== modified file 'database/schema/Makefile'
--- database/schema/Makefile	2019-02-25 22:58:26 +0000
+++ database/schema/Makefile	2019-05-22 15:20:07 +0000
@@ -27,7 +27,7 @@
 TEST_SESSION_DBNAME=session_ftest
 
 # The database which is a copy of launchpad_ftest_template and can be accessed
-# via http://launchpad.dev when you use LPCONFIG=test-playground.
+# via http://launchpad.test when you use LPCONFIG=test-playground.
 TEST_PLAYGROUND_DBNAME=launchpad_ftest_playground
 
 # The session database name.

=== modified file 'lib/canonical/launchpad/icing/css/forms.css'
--- lib/canonical/launchpad/icing/css/forms.css	2016-07-01 21:05:38 +0000
+++ lib/canonical/launchpad/icing/css/forms.css	2019-05-22 15:20:07 +0000
@@ -142,7 +142,7 @@
     }
 table.form, table.extra-options {
     /* Many forms are laid out using tables, with appropriate spacing: */
-    /* http://launchpad.dev/firefox/+edit */
+    /* http://launchpad.test/firefox/+edit */
     margin: 1em 0 inherit inherit;
     width: 100%;
     }
@@ -436,8 +436,8 @@
  * without using tables.
  *
  * Examples:
- *   https://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2/es/+upload
- *   https://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2/+export
+ *   https://translations.launchpad.test/evolution/trunk/+pots/evolution-2.2/es/+upload
+ *   https://translations.launchpad.test/evolution/trunk/+pots/evolution-2.2/+export
  */
 form.translations div.alignment .content {
     float: left;

=== modified file 'lib/canonical/launchpad/icing/style.css'
--- lib/canonical/launchpad/icing/style.css	2018-08-29 14:54:10 +0000
+++ lib/canonical/launchpad/icing/style.css	2019-05-22 15:20:07 +0000
@@ -812,8 +812,8 @@
 /* Templates listing.
  *
  * Examples:
- *   https://translations.launchpad.dev/ubuntu/hoary/+templates
- *   https://translations.launchpad.dev/evolution/trunk/+templates
+ *   https://translations.launchpad.test/ubuntu/hoary/+templates
+ *   https://translations.launchpad.test/evolution/trunk/+templates
  */
 .inactive-template td {
     background-color: #fee;
@@ -823,8 +823,8 @@
 /* Translations statistics and legend.
  *
  * Examples:
- *   https://translations.launchpad.dev/ubuntu/hoary/+lang/es
- *   https://translations.launchpad.dev/evolution/trunk/+lang/es
+ *   https://translations.launchpad.test/ubuntu/hoary/+lang/es
+ *   https://translations.launchpad.test/evolution/trunk/+lang/es
  */
 
 div.translations-legend {
@@ -857,9 +857,9 @@
 /* Translations help links.
  *
  * Examples:
- *   https://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/
- *   https://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+translations
- *   https://translations.launchpad.dev/ubuntu/hoary/
+ *   https://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/
+ *   https://translations.launchpad.test/ubuntu/hoary/+source/evolution/+translations
+ *   https://translations.launchpad.test/ubuntu/hoary/
  *
  */
 div.translation-help-links a {

=== modified file 'lib/launchpad_loggerhead/tests.py'
--- lib/launchpad_loggerhead/tests.py	2018-06-06 12:46:56 +0000
+++ lib/launchpad_loggerhead/tests.py	2019-05-22 15:20:07 +0000
@@ -202,7 +202,7 @@
             allow_redirects=False)
         self.assertEqual(301, response.status_code)
         self.assertEqual(
-            "testopenid.dev:8085",
+            "testopenid.test:8085",
             urlsplit(response.headers["Location"]).netloc)
 
     def test_private_port_public_branch(self):

=== modified file 'lib/lp/answers/browser/tests/faq-views.txt'
--- lib/lp/answers/browser/tests/faq-views.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/browser/tests/faq-views.txt	2019-05-22 15:20:07 +0000
@@ -45,10 +45,10 @@
 that the form works from any page.
 
     >>> print(view.portlet_action)
-    http://answers.launchpad.dev/firefox/+faqs
+    http://answers.launchpad.test/firefox/+faqs
 
     >>> print(content.form['action'])
-    http://answers.launchpad.dev/firefox/+faqs
+    http://answers.launchpad.test/firefox/+faqs
 
 The portlet provides a link to create a FAQ when the user that has append
 permission, such as the project owner.

=== modified file 'lib/lp/answers/doc/notifications.txt'
--- lib/lp/answers/doc/notifications.txt	2018-11-18 17:38:02 +0000
+++ lib/lp/answers/doc/notifications.txt	2019-05-22 15:20:07 +0000
@@ -622,13 +622,13 @@
 
     >>> print(notifications[0].body)
     Your question #... on Mozilla Firefox changed:
-    http://answers.launchpad.dev/firefox/+question/...
+    http://answers.launchpad.test/firefox/+question/...
     <BLANKLINE>
         Status: Open => Answered
     <BLANKLINE>
         Related FAQ set to:
         How do I install plugins (Shockwave, QuickTime, etc.)?
-        http://answers.launchpad.dev/firefox/+faq/10
+        http://answers.launchpad.test/firefox/+faq/10
     <BLANKLINE>
     Sample Person proposed the following answer:
     Read the FAQ.
@@ -643,11 +643,11 @@
 
     >>> print(notifications[0].body)
     Your question #... on Mozilla Firefox changed:
-    http://answers.launchpad.dev/firefox/+question/...
+    http://answers.launchpad.test/firefox/+question/...
     <BLANKLINE>
         Related FAQ was removed:
         How do I install plugins (Shockwave, QuickTime, etc.)?
-        http://answers.launchpad.dev/firefox/+faq/10
+        http://answers.launchpad.test/firefox/+faq/10
     <BLANKLINE>
     Sample Person proposed the following answer:
     Sorry, this wasn't so useful.

=== modified file 'lib/lp/answers/stories/answer-contact-report.txt'
--- lib/lp/answers/stories/answer-contact-report.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/answer-contact-report.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 the 'Answer Contact For' link from the actions portlet while viewing
 the Person's page.
 
-    >>> anon_browser.open('http://answers.launchpad.dev/~no-priv')
+    >>> anon_browser.open('http://answers.launchpad.test/~no-priv')
     >>> anon_browser.getLink('Answer contact for').click()
     >>> print(anon_browser.title)
     Projects for which...
@@ -20,7 +20,7 @@
 But when the person is an answer contact, the page displays the project
 they registered for.
 
-    >>> anon_browser.open('http://answers.launchpad.dev/~name16')
+    >>> anon_browser.open('http://answers.launchpad.test/~name16')
     >>> anon_browser.getLink('Answer contact for').click()
     >>> print(anon_browser.title)
     Projects for which...
@@ -49,7 +49,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     'http://answers.launchpad.dev/~name12')
+    ...     'http://answers.launchpad.test/~name12')
     >>> browser.getLink('Answer contact for').click()
     >>> print(browser.title)
     Projects for which...
@@ -68,7 +68,7 @@
 see the link for other users.
 
     >>> browser.open(
-    ...     'http://answers.launchpad.dev/~name16')
+    ...     'http://answers.launchpad.test/~name16')
     >>> browser.getLink('Answer contact for').click()
     >>> print(browser.title)
     Projects for which...

=== modified file 'lib/lp/answers/stories/distribution-package-answer-contact.txt'
--- lib/lp/answers/stories/distribution-package-answer-contact.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/distribution-package-answer-contact.txt	2019-05-22 15:20:07 +0000
@@ -26,7 +26,7 @@
 and another one listing those of the distribution.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/evolution')
+    ...     'http://launchpad.test/ubuntu/+source/evolution')
     >>> anon_browser.getLink('Answers').click()
     >>> portlet = find_portlet(
     ...     anon_browser.contents,
@@ -42,7 +42,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/evolution/+questions')
+    ...     'http://launchpad.test/ubuntu/+source/evolution/+questions')
     >>> browser.getLink('Answers').click()
     >>> browser.getLink('Set answer contact').click()
     >>> print(browser.title)

=== modified file 'lib/lp/answers/stories/faq-add.txt'
--- lib/lp/answers/stories/faq-add.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/faq-add.txt	2019-05-22 15:20:07 +0000
@@ -6,7 +6,7 @@
 No Privileges Person is not an answer contact for Mozilla Firefox, nor
 the project owner, therefore they cannot create a new FAQ.
 
-    >>> user_browser.open('http://answers.launchpad.dev/firefox')
+    >>> user_browser.open('http://answers.launchpad.test/firefox')
     >>> user_browser.getLink('All FAQs').click()
 
     >>> user_browser.getLink('Create a new FAQ')
@@ -15,7 +15,7 @@
     LinkNotFoundError
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+createfaq')
+    ...     'http://answers.launchpad.test/firefox/+createfaq')
     Traceback (most recent call last):
       ...
     Unauthorized: ...
@@ -26,7 +26,7 @@
 from various sources about the subject.
 
     >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
-    >>> owner_browser.open('http://answers.launchpad.dev/firefox')
+    >>> owner_browser.open('http://answers.launchpad.test/firefox')
     >>> owner_browser.getLink('All FAQs').click()
     >>> print(owner_browser.title)
     FAQs for Mozilla Firefox : Questions : Mozilla Firefox
@@ -46,7 +46,7 @@
 content field. They then submit the form using the 'Create' button.
 
     >>> owner_browser.url
-    'http://answers.launchpad.dev/firefox/+createfaq'
+    'http://answers.launchpad.test/firefox/+createfaq'
     >>> print(owner_browser.title)
     Create a FAQ for...
 
@@ -65,7 +65,7 @@
 The FAQ is created and the browser displays the page for Sample Person.
 
     >>> owner_browser.url
-    'http://answers.launchpad.dev/firefox/+faq/...'
+    'http://answers.launchpad.test/firefox/+faq/...'
     >>> print(owner_browser.title)
     FAQ #... : Questions : Mozilla Firefox
 

=== modified file 'lib/lp/answers/stories/faq-browse-and-search.txt'
--- lib/lp/answers/stories/faq-browse-and-search.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/faq-browse-and-search.txt	2019-05-22 15:20:07 +0000
@@ -21,14 +21,14 @@
     >>> transaction.commit()
     >>> logout()
 
-    >>> browser.open('http://answers.launchpad.dev/kubuntu')
+    >>> browser.open('http://answers.launchpad.test/kubuntu')
     >>> browser.getLink('All FAQs').click()
 
 Unfortunately, it seems that nobody has problems or questions about
 Kubuntu:
 
     >>> print(browser.url)
-    http://answers.launchpad.dev/kubuntu/+faqs
+    http://answers.launchpad.test/kubuntu/+faqs
     >>> print(browser.title)
     FAQs for Kubuntu : Questions : Kubuntu
 
@@ -41,7 +41,7 @@
 She learns through Fozzie Bear that support for Kubuntu is really
 happening on the Ubuntu project.
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu')
+    >>> browser.open('http://answers.launchpad.test/ubuntu')
     >>> browser.getLink('All FAQs').click()
     >>> print(browser.title)
     FAQs for Ubuntu : Questions : Ubuntu
@@ -83,7 +83,7 @@
     >>> print(backslashreplace(browser.title))
     FAQ #6 : Questions : Ubuntu
     >>> print(browser.url)
-    http://answers.launchpad.dev/ubuntu/+faq/6
+    http://answers.launchpad.test/ubuntu/+faq/6
 
 The FAQ page has a link back to the FAQ listing:
 
@@ -91,7 +91,7 @@
     >>> print(browser.title)
     FAQs for Ubuntu : Questions : Ubuntu
     >>> print(browser.url)
-    http://answers.launchpad.dev/ubuntu/+faqs
+    http://answers.launchpad.test/ubuntu/+faqs
 
 
 == Searching FAQs ==
@@ -166,25 +166,25 @@
 FAQs. The 'All FAQs' link that appears in that context links to the
 distribution FAQs.
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu/'
+    >>> browser.open('http://answers.launchpad.test/ubuntu/'
     ...              '+source/mozilla-firefox')
     >>> browser.getLink('All FAQs').click()
     >>> print(browser.title)
     FAQs for Ubuntu : Questions : Ubuntu
     >>> print(browser.url)
-    http://answers.launchpad.dev/ubuntu/+faqs
+    http://answers.launchpad.test/ubuntu/+faqs
 
 
 == Accessing an FAQ directly ==
 
 Asking for a non-existent FAQ or an invalid ID will raise a 404 error.
 
-    >>> anon_browser.open('http://answers.launchpad.dev/ubuntu/+faq/171717')
+    >>> anon_browser.open('http://answers.launchpad.test/ubuntu/+faq/171717')
     Traceback (most recent call last):
       ...
     NotFound: ...
 
-    >>> anon_browser.open('http://answers.launchpad.dev/ubuntu/+faq/bad')
+    >>> anon_browser.open('http://answers.launchpad.test/ubuntu/+faq/bad')
     Traceback (most recent call last):
       ...
     NotFound: ...

=== modified file 'lib/lp/answers/stories/faq-edit.txt'
--- lib/lp/answers/stories/faq-edit.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/faq-edit.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 appear for the anonymous user nor No Privileges Person:
 
     >>> from lp.services.helpers import backslashreplace
-    >>> anon_browser.open('http://answers.launchpad.dev/firefox/+faq/7')
+    >>> anon_browser.open('http://answers.launchpad.test/firefox/+faq/7')
     >>> print(backslashreplace(anon_browser.title))
     FAQ #7 : Questions : Mozilla Firefox
 
@@ -17,7 +17,7 @@
     ...
     LinkNotFoundError
 
-    >>> user_browser.open('http://answers.launchpad.dev/firefox/+faq/7')
+    >>> user_browser.open('http://answers.launchpad.test/firefox/+faq/7')
     >>> user_browser.getLink('Edit FAQ')
     Traceback (most recent call last):
     ...
@@ -26,12 +26,12 @@
 Even trying to access the link directly will fail:
 
     >>> anon_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+faq/7/+edit')
+    ...     'http://answers.launchpad.test/firefox/+faq/7/+edit')
     Traceback (most recent call last):
     ...
     Unauthorized: ...
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+faq/7/+edit')
+    ...     'http://answers.launchpad.test/firefox/+faq/7/+edit')
     Traceback (most recent call last):
     ...
     Unauthorized: ...
@@ -40,10 +40,10 @@
 project:
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://answers.launchpad.dev/firefox/+faq/7')
+    >>> browser.open('http://answers.launchpad.test/firefox/+faq/7')
     >>> browser.getLink('Edit FAQ').click()
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+faq/7/+edit
+    http://answers.launchpad.test/firefox/+faq/7/+edit
     >>> print(browser.title)
     Edit FAQ...
 
@@ -68,7 +68,7 @@
 The user can see their changes on the page:
 
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+faq/7
+    http://answers.launchpad.test/firefox/+faq/7
 
     >>> print(extract_text(find_tag_by_id(browser.contents, 'faq-keywords')))
     Keywords: windows ubuntu plugins extensions

=== modified file 'lib/lp/answers/stories/project-add-question.txt'
--- lib/lp/answers/stories/project-add-question.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/project-add-question.txt	2019-05-22 15:20:07 +0000
@@ -19,13 +19,13 @@
 The logged user will see the Ask a Question page, for the Mozilla
 Project in this case.
 
-    >>> anon_browser.open('http://answers.launchpad.dev/mozilla')
+    >>> anon_browser.open('http://answers.launchpad.test/mozilla')
     >>> anon_browser.getLink('Ask a question').click()
     Traceback (most recent call last):
       ...
     Unauthorized...
 
-    >>> user_browser.open('http://answers.launchpad.dev/mozilla')
+    >>> user_browser.open('http://answers.launchpad.test/mozilla')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question...
@@ -155,7 +155,7 @@
 speaks Japanese, so we will use him.
 
     >>> daf_browser = setupBrowser(auth='Basic daf@xxxxxxxxxxxxx:test')
-    >>> daf_browser.open('http://launchpad.dev/~daf/+editlanguages')
+    >>> daf_browser.open('http://launchpad.test/~daf/+editlanguages')
     >>> print(daf_browser.title)
     Language preferences...
 
@@ -163,7 +163,7 @@
     True
 
     >>> daf_browser.open(
-    ...     'http://answers.launchpad.dev/thunderbird/+answer-contact')
+    ...     'http://answers.launchpad.test/thunderbird/+answer-contact')
     >>> print(daf_browser.title)
     Answer contact for...
 
@@ -181,7 +181,7 @@
 match. This condition demonstrates the supported language behaviour.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/~no-priv/+editlanguages')
+    ...     'http://launchpad.test/~no-priv/+editlanguages')
     >>> print(user_browser.title)
     Language preferences...
 
@@ -198,7 +198,7 @@
 and expect someone to reply in the same language.
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+addquestion')
+    ...     'http://answers.launchpad.test/firefox/+addquestion')
     >>> print(user_browser.getControl('Language').displayOptions)
     ['English (en) *', 'Japanese (ja)']
 
@@ -225,7 +225,7 @@
 No Privileged Person did above, but this time in wants to do so in
 Japanese.
 
-    >>> user_browser.open('http://answers.launchpad.dev/mozilla')
+    >>> user_browser.open('http://answers.launchpad.test/mozilla')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question...
@@ -295,7 +295,7 @@
 Privileges Person correctly chooses Thunderbird as the subject of their
 question.
 
-    >>> user_browser.open('http://answers.launchpad.dev/mozilla')
+    >>> user_browser.open('http://answers.launchpad.test/mozilla')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question...

=== modified file 'lib/lp/answers/stories/question-add-in-other-languages.txt'
--- lib/lp/answers/stories/question-add-in-other-languages.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-add-in-other-languages.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 'Ask a question' page has a pop-up where the user can select the language
 of the question. By default, the question language is 'English'.
 
-    >>> user_browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> user_browser.open('http://launchpad.test/ubuntu/+questions')
     >>> user_browser.getLink('Ask a question').click()
     >>> user_browser.getControl('Language').value
     ['en']
@@ -21,7 +21,7 @@
 The languages that are supported are displayed with an asterisk.
 
     >>> browser.addHeader('Authorization', 'Basic salgado@xxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.dev/ubuntu/+addquestion')
+    >>> browser.open('http://launchpad.test/ubuntu/+addquestion')
 
     >>> browser.getControl('Language').displayOptions
     ['English (en) *', 'Portuguese (Brazil) (pt_BR)']
@@ -104,7 +104,7 @@
 then changed their mind on the second page.
 
     >>> browser = setupBrowser(auth='Basic daf@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.dev/ubuntu/+addquestion')
+    >>> browser.open('http://launchpad.test/ubuntu/+addquestion')
 
     >>> browser.getControl('Language').value = ['en']
     >>> browser.getControl('Summary').value = 'some random words'
@@ -126,7 +126,7 @@
     >>> browser.getControl('Post Question').click()
 
     >>> browser.url
-    'http://launchpad.dev/ubuntu/+addquestion'
+    'http://launchpad.test/ubuntu/+addquestion'
 
     >>> for tag in find_tags_by_class(browser.contents, 'warning message'):
     ...     print(tag.renderContents())

=== modified file 'lib/lp/answers/stories/question-add.txt'
--- lib/lp/answers/stories/question-add.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-add.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 one involes two steps. First, go to the product or distribution for
 which support is desired:
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu')
+    >>> browser.open('http://answers.launchpad.test/ubuntu')
     >>> print(browser.title)
     Questions : Ubuntu
 
@@ -21,7 +21,7 @@
     Traceback (most recent call last):
       ...
     Unauthorized...
-    >>> user_browser.open('http://answers.launchpad.dev/ubuntu/')
+    >>> user_browser.open('http://answers.launchpad.test/ubuntu/')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question...
@@ -37,7 +37,7 @@
 associated with the source package of the used application.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/ubuntu/hoary/'
+    ...     'http://launchpad.test/ubuntu/hoary/'
     ...     '+sources/mozilla-firefox/+gethelp')
     >>> print(user_browser.title)
     Help and support...
@@ -86,7 +86,7 @@
     How can I play MP3/Divx/DVDs/Quicktime/Realmedia files or view
         Flash/Java web pages
     >>> similar_faqs.a['href']
-    u'http://answers.launchpad.dev/ubuntu/+faq/...'
+    u'http://answers.launchpad.test/ubuntu/+faq/...'
 
     >>> similar_questions = contents.find(id='similar-questions')
     >>> print(extract_text(similar_questions).encode(

=== modified file 'lib/lp/answers/stories/question-answer-contact.txt'
--- lib/lp/answers/stories/question-answer-contact.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-answer-contact.txt	2019-05-22 15:20:07 +0000
@@ -11,7 +11,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
     >>> browser.addHeader('Accept-Language', 'en, es')
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> print(extract_text(
     ...     find_tag_by_id(
     ...         browser.contents, 'portlet-answer-contacts-ubuntu')))
@@ -68,7 +68,7 @@
 answer contact' link and uncheck themselves or the team they want to remove
 from the answer contact list.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> browser.getLink('Set answer contact').click()
     >>> browser.getControl(
     ...     "I want to be an answer contact for Ubuntu").selected
@@ -92,13 +92,13 @@
 
 The 'Set answer contact' action is also available on products:
 
-    >>> browser.open('http://answers.launchpad.dev/firefox')
+    >>> browser.open('http://answers.launchpad.test/firefox')
     >>> browser.getLink('Set answer contact').click()
     >>> print(browser.title)
     Answer contact for...
 
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+answer-contact
+    http://answers.launchpad.test/firefox/+answer-contact
 
 
 Answer Contact teams and preferred languages
@@ -115,7 +115,7 @@
 contacts for Ubuntu, but only for Spanish questions to keep the email
 traffic to a manageable volume.
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu/')
+    >>> browser.open('http://answers.launchpad.test/ubuntu/')
     >>> print(browser.title)
     Questions : Ubuntu
 
@@ -129,7 +129,7 @@
 Sample Person navigates to the team page to set it's preferred
 languages. They must add Spanish to the team's preferred languages.
 
-    >>> browser.open('http://launchpad.dev/~landscape-developers')
+    >>> browser.open('http://launchpad.test/~landscape-developers')
     >>> browser.title
     'Landscape Developers in Launchpad'
 

=== modified file 'lib/lp/answers/stories/question-answers-vhost.txt'
--- lib/lp/answers/stories/question-answers-vhost.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-answers-vhost.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 Product
 -------
 
-    >>> anon_browser.open('http://answers.launchpad.dev/firefox')
+    >>> anon_browser.open('http://answers.launchpad.test/firefox')
     >>> print(anon_browser.title)
     Questions : Mozilla Firefox
 
@@ -17,7 +17,7 @@
 Distribution
 ------------
 
-    >>> anon_browser.open('http://answers.launchpad.dev/ubuntu')
+    >>> anon_browser.open('http://answers.launchpad.test/ubuntu')
     >>> print(anon_browser.title)
     Questions : Ubuntu
 
@@ -26,7 +26,7 @@
 ---------------------------
 
     >>> anon_browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+source/mozilla-firefox')
+    ...     'http://answers.launchpad.test/ubuntu/+source/mozilla-firefox')
     >>> print(anon_browser.title)
     Questions : mozilla-firefox package : Ubuntu
 
@@ -35,7 +35,7 @@
 ------------
 
     >>> anon_browser.open(
-    ...     'http://answers.launchpad.dev/mozilla')
+    ...     'http://answers.launchpad.test/mozilla')
     >>> print(anon_browser.title)
     Questions : The Mozilla Project
 
@@ -43,6 +43,6 @@
 Person
 ------
 
-    >>> anon_browser.open('http://answers.launchpad.dev/~name16')
+    >>> anon_browser.open('http://answers.launchpad.test/~name16')
     >>> print(anon_browser.title)
     Questions : Foo Bar

=== modified file 'lib/lp/answers/stories/question-browse-and-search.txt'
--- lib/lp/answers/stories/question-browse-and-search.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-browse-and-search.txt	2019-05-22 15:20:07 +0000
@@ -21,7 +21,7 @@
     >>> transaction.commit()
     >>> logout()
 
-    >>> browser.open('http://launchpad.dev/kubuntu')
+    >>> browser.open('http://launchpad.test/kubuntu')
     >>> browser.getLink('Answers').click()
 
 He discovers that there are no questions on the Kubuntu Answers page:
@@ -34,7 +34,7 @@
 
 For projects that don't have products, the Answers facet is disabled.
 
-    >>> browser.open('http://launchpad.dev/aaa')
+    >>> browser.open('http://launchpad.test/aaa')
     >>> browser.getLink('Answers')
     Traceback (most recent call last):
      ...
@@ -45,7 +45,7 @@
 He realises that support for Kubuntu is probably going on in the Ubuntu
 Answers page and goes there to check.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> print(browser.title)
     Questions : Ubuntu
 
@@ -144,7 +144,7 @@
 Launchpad Answer Tracker. He visits the main page and enters '9'
 to jump to the question.
 
-    >>> browser.open('http://answers.launchpad.dev/')
+    >>> browser.open('http://answers.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = '9'
     >>> browser.getControl('Find Answers').click()
     >>> from lp.services.helpers import backslashreplace
@@ -157,7 +157,7 @@
 and pastes it into the main page of the Answer Tracker to read
 the answer.
 
-    >>> browser.open('http://answers.launchpad.dev/')
+    >>> browser.open('http://answers.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = ' #6 '
     >>> browser.getControl('Find Answers').click()
     >>> print(backslashreplace(browser.title))
@@ -168,7 +168,7 @@
 and pastes it into the text field on the Answer Tracker main page. He
 is shown search results instead of the question.
 
-    >>> browser.open('http://answers.launchpad.dev/')
+    >>> browser.open('http://answers.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = 'question 8'
     >>> browser.getControl('Find Answers').click()
     >>> print(browser.title)
@@ -189,7 +189,7 @@
 similar questions is easy: on the question listing page, he just
 enters his search key and hit the 'Search' button.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> browser.getControl(name='field.search_text').value = 'firefox is slow'
     >>> browser.getControl('Search', index=0).click()
 
@@ -232,7 +232,7 @@
 The user must choose at least one status when searching questions. An
 error is displayed when the user forgets to select a status.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> browser.getControl(name='field.status').displayValue = []
     >>> browser.getControl('Search', index=0).click()
     >>> messages = find_tags_by_class(browser.contents, 'message')
@@ -251,7 +251,7 @@
     # We should use goBack() here but can't because of bug #98372:
     # zope.testbrowser truncates document content after goBack().
     #>>> browser.goBack()
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> browser.getLink('mozilla-firefox').click()
     >>> browser.title
     'Questions : mozilla-firefox package : Ubuntu'
@@ -289,7 +289,7 @@
 Nice Guy likes helping others. He uses the 'Open' link to view the most
 recent questions on Mozilla Firefox.
 
-    >>> browser.open('http://launchpad.dev/firefox/+questions')
+    >>> browser.open('http://launchpad.test/firefox/+questions')
     >>> browser.getLink('Open').click()
     >>> print(browser.title)
     Questions : Mozilla Firefox
@@ -326,7 +326,7 @@
 similar problems. (This listing includes both 'Answered' and 'Solved'
 questions.)
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/ubuntu/+questions')
     >>> browser.getLink('Answered').click()
     >>> print(browser.title)
     Questions : Ubuntu
@@ -362,7 +362,7 @@
 They need to login to access that page:
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/'
+    ...     'http://launchpad.test/ubuntu/+source/mozilla-firefox/'
     ...     '+questions')
     >>> anon_browser.getLink('My questions').click()
     Traceback (most recent call last):
@@ -372,7 +372,7 @@
     >>> sample_person_browser = setupBrowser(
     ...     auth='Basic test@xxxxxxxxxxxxx:test')
     >>> sample_person_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/'
+    ...     'http://launchpad.test/ubuntu/+source/mozilla-firefox/'
     ...     '+questions')
     >>> sample_person_browser.getLink('My questions').click()
     >>> print(repr(sample_person_browser.title))
@@ -417,7 +417,7 @@
     >>> logout()
 
     >>> sample_person_browser.open(
-    ...     'http://launchpad.dev/gnomebaker/+questions')
+    ...     'http://launchpad.test/gnomebaker/+questions')
     >>> sample_person_browser.getLink('My questions').click()
     >>> print(find_main_content(
     ...     sample_person_browser.contents).find('p').renderContents())
@@ -434,14 +434,14 @@
 
 They need to login to access that page:
 
-    >>> anon_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> anon_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> anon_browser.getLink('Need attention').click()
     Traceback (most recent call last):
       ...
     Unauthorized...
 
     >>> sample_person_browser.open(
-    ...     'http://launchpad.dev/distros/ubuntu/+questions')
+    ...     'http://launchpad.test/distros/ubuntu/+questions')
     >>> sample_person_browser.getLink('Need attention').click()
     >>> print(sample_person_browser.title)
     Questions needing your attention for Ubuntu : Questions : Ubuntu
@@ -469,7 +469,7 @@
 informing them of this fact is displayed.
 
     >>> sample_person_browser.open(
-    ...    'http://launchpad.dev/products/gnomebaker/+questions')
+    ...    'http://launchpad.test/products/gnomebaker/+questions')
     >>> sample_person_browser.getLink('Need attention').click()
     >>> print(find_main_content(
     ...     sample_person_browser.contents).find('p').renderContents())
@@ -486,7 +486,7 @@
 the person asked, answered, is assigned to, is subscribed to, or
 commented on.
 
-    >>> browser.open('http://launchpad.dev/~name16')
+    >>> browser.open('http://launchpad.test/~name16')
     >>> browser.getLink('Answers').click()
     >>> print(browser.title)
     Questions : Foo Bar
@@ -526,7 +526,7 @@
 questions to a particular status:
 
     # goBack() doesn't work.
-    >>> browser.open('http://launchpad.dev/~name16/+questions')
+    >>> browser.open('http://launchpad.test/~name16/+questions')
     >>> browser.getControl(name='field.search_text').value = 'Firefox'
     >>> browser.getControl(name='field.status').displayValue = [
     ...     b'Solved', b'Invalid']
@@ -634,7 +634,7 @@
 When going to the Answers facet of a project, a listing of all the
 questions filed against any of the project's products is displayed.
 
-    >>> browser.open('http://launchpad.dev/mozilla')
+    >>> browser.open('http://launchpad.test/mozilla')
     >>> browser.getLink('Answers').click()
     >>> print(browser.title)
     Questions : The Mozilla Project
@@ -677,7 +677,7 @@
     Questions : The Mozilla Project
 
     # The next two reports are only available to a logged-in user.
-    >>> user_browser.open('http://launchpad.dev/mozilla/+questions')
+    >>> user_browser.open('http://launchpad.test/mozilla/+questions')
     >>> user_browser.getLink('My questions').click()
     >>> print(user_browser.title)
     Questions you asked about The Mozilla Project : Questions : The Mozilla Project
@@ -692,7 +692,7 @@
 It is possible from the Answer Tracker front page to search among all
 questions ever filed on Launchpad.
 
-    >>> browser.open('http://answers.launchpad.dev/')
+    >>> browser.open('http://answers.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = 'firefox'
     >>> browser.getControl('Find Answers').click()
 
@@ -700,7 +700,7 @@
     Questions matching "firefox"
 
     >>> print(browser.url)
-    http://answers.launchpad.dev/questions/+questions?...
+    http://answers.launchpad.test/questions/+questions?...
 
 The results are displayed in a format similar to the Person reports:
 there is an 'In' column displaying where the questions were filed.
@@ -750,7 +750,7 @@
 
 They must enter the project's name in the text field:
 
-    >>> anon_browser.open('http://answers.launchpad.dev')
+    >>> anon_browser.open('http://answers.launchpad.test')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl('Find Answers').click()
 
@@ -773,12 +773,12 @@
 
     >>> find_link = anon_browser.getLink('Find')
     >>> print(find_link.url)
-    http://answers.launchpad.dev/questions...
+    http://answers.launchpad.test/questions...
 
 
 The form field can be filled manually without using the ajax widget.
 
-    >>> anon_browser.open('http://answers.launchpad.dev')
+    >>> anon_browser.open('http://answers.launchpad.test')
     >>> anon_browser.getControl(name='field.search_text').value = 'plugins'
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'mozilla'
@@ -788,7 +788,7 @@
 
 This works also with distributions:
 
-    >>> anon_browser.open('http://answers.launchpad.dev')
+    >>> anon_browser.open('http://answers.launchpad.test')
     >>> anon_browser.getControl(name='field.search_text').value = 'firefox'
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'ubuntu'
@@ -798,7 +798,7 @@
 
 And also with products:
 
-    >>> anon_browser.open('http://answers.launchpad.dev')
+    >>> anon_browser.open('http://answers.launchpad.test')
     >>> anon_browser.getControl(name='field.search_text').value = 'plugins'
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'firefox'

=== modified file 'lib/lp/answers/stories/question-compatibility-urls.txt'
--- lib/lp/answers/stories/question-compatibility-urls.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-compatibility-urls.txt	2019-05-22 15:20:07 +0000
@@ -6,67 +6,67 @@
 
 == Answer Contact Page ==
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+support-contact')
+    >>> user_browser.open('http://launchpad.test/firefox/+support-contact')
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/firefox/+answer-contact
+    http://answers.launchpad.test/firefox/+answer-contact
 
 == Add Question Page ==
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+addticket')
+    >>> user_browser.open('http://launchpad.test/firefox/+addticket')
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/firefox/+addquestion
+    http://answers.launchpad.test/firefox/+addquestion
 
-    >>> user_browser.open('http://launchpad.dev/mozilla/+addticket')
+    >>> user_browser.open('http://launchpad.test/mozilla/+addticket')
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/mozilla/+addquestion
+    http://answers.launchpad.test/mozilla/+addquestion
 
 == My Questions Page ==
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+mytickets')
+    >>> user_browser.open('http://launchpad.test/firefox/+mytickets')
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/firefox/+myquestions
+    http://answers.launchpad.test/firefox/+myquestions
 
 == Questions Listing ==
 
-    >>> browser.open('http://launchpad.dev/firefox/+tickets')
+    >>> browser.open('http://launchpad.test/firefox/+tickets')
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+questions
+    http://answers.launchpad.test/firefox/+questions
 
 == Question Page ==
 
-    >>> browser.open('http://launchpad.dev/firefox/+ticket/1')
+    >>> browser.open('http://launchpad.test/firefox/+ticket/1')
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+question/1
+    http://answers.launchpad.test/firefox/+question/1
 
 == Person Questions Listing ==
 
-    >>> browser.open('http://launchpad.dev/~name12/+tickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+questions
-
-    >>> browser.open('http://launchpad.dev/~name12/+answeredtickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+answeredquestions
-
-    >>> browser.open('http://launchpad.dev/~name12/+assignedtickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+assignedquestions
-
-    >>> browser.open('http://launchpad.dev/~name12/+commentedtickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+commentedquestions
-
-    >>> browser.open('http://launchpad.dev/~name12/+createdtickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+createdquestions
-
-    >>> browser.open('http://launchpad.dev/~name12/+needattentiontickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+needattentionquestions
-
-    >>> browser.open('http://launchpad.dev/~name12/+subscribedtickets')
-    >>> print(browser.url)
-    http://answers.launchpad.dev/~name12/+subscribedquestions
+    >>> browser.open('http://launchpad.test/~name12/+tickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+questions
+
+    >>> browser.open('http://launchpad.test/~name12/+answeredtickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+answeredquestions
+
+    >>> browser.open('http://launchpad.test/~name12/+assignedtickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+assignedquestions
+
+    >>> browser.open('http://launchpad.test/~name12/+commentedtickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+commentedquestions
+
+    >>> browser.open('http://launchpad.test/~name12/+createdtickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+createdquestions
+
+    >>> browser.open('http://launchpad.test/~name12/+needattentiontickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+needattentionquestions
+
+    >>> browser.open('http://launchpad.test/~name12/+subscribedtickets')
+    >>> print(browser.url)
+    http://answers.launchpad.test/~name12/+subscribedquestions
 
 
 == Unsupported questions ==
@@ -74,9 +74,9 @@
 The Unsupported View is irrelevant. The question search page provides
 links to the +by-language pages that have unsolved questions.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+unsupported')
+    >>> browser.open('http://launchpad.test/ubuntu/+unsupported')
     >>> print(browser.url)
-    http://answers.launchpad.dev/ubuntu/+questions
+    http://answers.launchpad.test/ubuntu/+questions
 
 
 == Enumeration changes in search URLs ==
@@ -90,7 +90,7 @@
 
     >>> old_sort = 'by+status'
     >>> old_status = 'Needs+information'
-    >>> url = ('http://answers.launchpad.dev/ubuntu/+questions'
+    >>> url = ('http://answers.launchpad.test/ubuntu/+questions'
     ...        '?field.sort=%s&field.sort-empty-marker=1'
     ...        '&field.language=en&field.language-empty-marker=1'
     ...        '&field.search_text=&field.actions.search=Search'

=== modified file 'lib/lp/answers/stories/question-edit.txt'
--- lib/lp/answers/stories/question-edit.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-edit.txt	2019-05-22 15:20:07 +0000
@@ -5,22 +5,22 @@
 only the question creator or an owner of the question target can change the
 title and description.
 
-    >>> anon_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> anon_browser.open('http://launchpad.test/firefox/+question/2')
     >>> anon_browser.getLink('Edit question').click()
     Traceback (most recent call last):
     ...
     Unauthorized...
 
     >>> test_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
-    >>> test_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> test_browser.open('http://launchpad.test/firefox/+question/2')
     >>> test_browser.getLink('Edit question').click()
     >>> print(test_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2/+edit
+    http://answers.launchpad.test/firefox/+question/2/+edit
 
 There is a cancel link should the user decide otherwise:
 
     >>> print(test_browser.getLink('Cancel').url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 When we post the form, we should be redirected back to the question page.
 
@@ -33,7 +33,7 @@
     >>> test_browser.getControl('Save Changes').click()
 
     >>> print(test_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 And viewing that page should show the updated information.
 
@@ -52,7 +52,7 @@
     ...     print(extract_text(
     ...         find_tag_by_id(browser.contents, 'question-status')))
 
-    >>> test_browser.open('http://launchpad.dev/ubuntu/+question/3')
+    >>> test_browser.open('http://launchpad.test/ubuntu/+question/3')
     >>> print_question_status(test_browser)
     Status: Invalid
 
@@ -66,7 +66,7 @@
 Any logged in user can change the question source package on the
 'Edit Question' page.
 
-    >>> user_browser.open('http://launchpad.dev/ubuntu/+question/5')
+    >>> user_browser.open('http://launchpad.test/ubuntu/+question/5')
     >>> user_browser.getLink('Edit question').click()
     >>> user_browser.getControl(
     ...     name='field.target.package').value = 'linux-source-2.6.15'
@@ -74,7 +74,7 @@
 
 Product questions ignore sourcepackage information if it is submitted:
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> user_browser.open('http://launchpad.test/firefox/+question/2')
     >>> user_browser.getLink('Edit question').click()
     >>> user_browser.getControl(
     ...     name='field.target.package').value = 'linux-source-2.6.15'

=== modified file 'lib/lp/answers/stories/question-message.txt'
--- lib/lp/answers/stories/question-message.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-message.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 an email post to examine the markup rules. This message contains a
 quoted passage, and a signature with an email address in it.
 
-    >>> user_browser.open('http://answers.launchpad.dev/ubuntu/+question/11')
+    >>> user_browser.open('http://answers.launchpad.test/ubuntu/+question/11')
     >>> print(user_browser.title.decode('utf-8'))
     Question #11 : ...
 
@@ -50,7 +50,7 @@
 of 'person@xxxxxxxxxx'. The anonymous user is unauthenticated, so they will
 see the obfuscated email address (<email address hidden>).
 
-    >>> anon_browser.open('http://answers.launchpad.dev/ubuntu/+question/11')
+    >>> anon_browser.open('http://answers.launchpad.test/ubuntu/+question/11')
     >>> print(anon_browser.title)
     Question #11 : ...
 

=== modified file 'lib/lp/answers/stories/question-obfuscation.txt'
--- lib/lp/answers/stories/question-obfuscation.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-obfuscation.txt	2019-05-22 15:20:07 +0000
@@ -12,7 +12,7 @@
 questions in the Latest questions solved portlet on the Answers
 front page.
 
-    >>> user_browser.open('http://answers.launchpad.dev/')
+    >>> user_browser.open('http://answers.launchpad.test/')
     >>> question_portlet = find_tag_by_id(
     ...     user_browser.contents, 'latest-questions-solved')
     >>> for li in question_portlet.findAll('li'):
@@ -78,7 +78,7 @@
     >>> print(user_browser.title)
     Question #... : ...
 
-    >>> user_browser.open('http://answers.launchpad.dev/')
+    >>> user_browser.open('http://answers.launchpad.test/')
     >>> question_portlet = find_tag_by_id(
     ...     user_browser.contents, 'latest-questions-asked')
     >>> for li in question_portlet.findAll('li'):
@@ -92,7 +92,7 @@
 Anonymous cannot see the email address anywhere on the Answers front
 page.
 
-    >>> anon_browser.open('http://answers.launchpad.dev/')
+    >>> anon_browser.open('http://answers.launchpad.test/')
     >>> b'user@xxxxxxxxxx' in anon_browser.contents
     False
 

=== modified file 'lib/lp/answers/stories/question-overview.txt'
--- lib/lp/answers/stories/question-overview.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-overview.txt	2019-05-22 15:20:07 +0000
@@ -12,7 +12,7 @@
 Creating a new question is possible directly from the main page of a
 product. Users simply click the 'Ask a question' buttion.
 
-    >>> user_browser.open('http://launchpad.dev/firefox')
+    >>> user_browser.open('http://launchpad.test/firefox')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question about...
@@ -20,7 +20,7 @@
 There is also an 'Ask a question' link on the Bugs home page of the
 product:
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question about...
@@ -28,7 +28,7 @@
 The list of all the currently active questions for the product is
 available from the 'Answers' facet.
 
-    >>> browser.open('http://launchpad.dev/firefox')
+    >>> browser.open('http://launchpad.test/firefox')
     >>> browser.getLink('Answers').click()
 
     >>> soup = find_main_content(browser.contents)
@@ -41,7 +41,7 @@
 There is also an 'Ask a question' link to ask a new question.
 
     >>> browser.getLink('Ask a question').url
-    'http://answers.launchpad.dev/firefox/+addquestion'
+    'http://answers.launchpad.test/firefox/+addquestion'
 
 There are links to some common listing of questions:
 
@@ -70,23 +70,23 @@
 
 Distributions have an 'Ask a question' link on the front page:
 
-    >>> user_browser.open('http://launchpad.dev/ubuntu')
+    >>> user_browser.open('http://launchpad.test/ubuntu')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question about...
 
 As well as on the Bugs facet home page:
 
-    >>> user_browser.open('http://bugs.launchpad.dev/ubuntu')
+    >>> user_browser.open('http://bugs.launchpad.test/ubuntu')
     >>> user_browser.getLink('Ask a question').click()
     >>> print(user_browser.title)
     Ask a question about...
 
 The distribution home page also has a link to the 'Answers' facet:
 
-    >>> browser.open('http://launchpad.dev/ubuntu')
+    >>> browser.open('http://launchpad.test/ubuntu')
     >>> browser.getLink('Answers').url
-    'http://answers.launchpad.dev/ubuntu'
+    'http://answers.launchpad.test/ubuntu'
 
     >>> browser.getLink('Answers').click()
     >>> print(browser.title)
@@ -113,9 +113,9 @@
 On a source package, the 'Ask a question' link is accessible through the
 Answers facet.
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+source/evolution')
+    >>> browser.open('http://launchpad.test/ubuntu/+source/evolution')
     >>> browser.getLink('Answers').url
-    'http://answers.launchpad.dev/ubuntu/+source/evolution'
+    'http://answers.launchpad.test/ubuntu/+source/evolution'
 
     >>> browser.getLink('Answers').click()
     >>> print(browser.title)
@@ -138,9 +138,9 @@
 The 'Answers' facet is also available on the distribution source package
 page:
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+source/mozilla-firefox')
+    >>> browser.open('http://launchpad.test/ubuntu/+source/mozilla-firefox')
     >>> browser.getLink('Answers').url
-    'http://answers.launchpad.dev/ubuntu/+source/mozilla-firefox'
+    'http://answers.launchpad.test/ubuntu/+source/mozilla-firefox'
 
     >>> browser.getLink('Answers').click()
     >>> browser.title
@@ -165,7 +165,7 @@
 ProjectGroups also have the 'Latest questions' portlet and the 'Ask a
 question' button on their overview page.
 
-    >>> user_browser.open('http://launchpad.dev/mozilla')
+    >>> user_browser.open('http://launchpad.test/mozilla')
 
     >>> questions = find_tag_by_id(
     ...     user_browser.contents, 'portlet-latest-questions')
@@ -189,9 +189,9 @@
 The 'Answers' facet link will display a page listing all the questions
 involving a person.
 
-    >>> browser.open('http://launchpad.dev/~name16')
+    >>> browser.open('http://launchpad.test/~name16')
     >>> browser.getLink('Answers').url
-    'http://answers.launchpad.dev/~name16'
+    'http://answers.launchpad.test/~name16'
 
     >>> browser.getLink('Answers').click()
     >>> print(browser.title)
@@ -215,12 +215,12 @@
 -----------------------------
 
 You can access any question by its ID using the URL
-http://answers.launchpad.dev/questions/<id>. This URL will redirect to
+http://answers.launchpad.test/questions/<id>. This URL will redirect to
 the proper context where the question can be found:
 
-    >>> browser.open('http://answers.launchpad.dev/questions/1')
+    >>> browser.open('http://answers.launchpad.test/questions/1')
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+question/1
+    http://answers.launchpad.test/firefox/+question/1
 
     >>> print(find_main_content(browser.contents).find('h1').renderContents())
     Firefox cannot render Bank Site
@@ -228,12 +228,12 @@
 Asking for a non-existent question or an invalid ID will still raise a
 404 though:
 
-    >>> browser.open('http://answers.launchpad.dev/questions/255')
+    >>> browser.open('http://answers.launchpad.test/questions/255')
     Traceback (most recent call last):
       ...
     NotFound: ...
 
-    >>> browser.open('http://answers.launchpad.dev/questions/bad_id')
+    >>> browser.open('http://answers.launchpad.test/questions/bad_id')
     Traceback (most recent call last):
       ...
     NotFound: ...
@@ -242,26 +242,26 @@
 redirected to the question in the proper context. (For example, this is
 useful after a question was retargeted.)
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu/+question/1')
+    >>> browser.open('http://answers.launchpad.test/ubuntu/+question/1')
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+question/1
+    http://answers.launchpad.test/firefox/+question/1
 
 It also works with pages below that URL:
 
     >>> browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+question/1/+history')
+    ...     'http://answers.launchpad.test/ubuntu/+question/1/+history')
     >>> print(browser.url)
-    http://answers.launchpad.dev/firefox/+question/1/+history
+    http://answers.launchpad.test/firefox/+question/1/+history
 
 But again, an invalid ID still raises a 404:
 
-    >>> browser.open('http://answers.launchpad.dev/ubuntu/+question/255')
+    >>> browser.open('http://answers.launchpad.test/ubuntu/+question/255')
     Traceback (most recent call last):
       ...
     NotFound: ...
 
     >>> browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+question/bad_id')
+    ...     'http://answers.launchpad.test/ubuntu/+question/bad_id')
     Traceback (most recent call last):
       ...
     NotFound: ...

=== modified file 'lib/lp/answers/stories/question-reject-and-change-status.txt'
--- lib/lp/answers/stories/question-reject-and-change-status.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-reject-and-change-status.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 No Privileges Person isn't an answer contact or administrator, so they
 don't have access to that feature.
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> user_browser.open('http://launchpad.test/firefox/+question/2')
     >>> user_browser.getLink('Reject question')
     Traceback (most recent call last):
       ...
@@ -17,14 +17,14 @@
 error.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2/+reject')
+    ...     'http://launchpad.test/firefox/+question/2/+reject')
     Traceback (most recent call last):
       ...
     Unauthorized: ...
 
 To reject the question, the user clicks on the 'Reject Question' link.
 
-    >>> admin_browser.open('http://answers.launchpad.dev/firefox/+question/2')
+    >>> admin_browser.open('http://answers.launchpad.test/firefox/+question/2')
     >>> admin_browser.getLink('Reject question').click()
     >>> admin_browser.getControl('Reject').click()
 
@@ -39,7 +39,7 @@
 cancel link to take them back to the question:
 
     >>> print(admin_browser.getLink('Cancel').url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 Entering an explanation message and clicking the 'Reject' button,
 will reject the question.
@@ -79,7 +79,7 @@
 
     >>> admin_browser.getLink('Reject question').click()
     >>> print(admin_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
     >>> for message in find_tags_by_class(admin_browser.contents, 'message'):
     ...     print(message.renderContents())
     The question is already rejected.
@@ -97,7 +97,7 @@
 
 That action isn't available to a non-privileged user:
 
-    >>> browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> browser.open('http://launchpad.test/firefox/+question/2')
     >>> browser.getLink('Change status')
     Traceback (most recent call last):
       ...
@@ -106,7 +106,7 @@
 The change status form is available to an administrator through the
 'Change status' link.
 
-    >>> admin_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> admin_browser.open('http://launchpad.test/firefox/+question/2')
     >>> admin_browser.getLink('Change status').click()
 
 The form has a select widget displaying the current status.
@@ -117,7 +117,7 @@
 There is also a cancel link should the user decide otherwise:
 
     >>> print(admin_browser.getLink('Cancel').url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 The user needs to select a status and enter a message explaining the
 status change:

=== modified file 'lib/lp/answers/stories/question-search-multiple-languages.txt'
--- lib/lp/answers/stories/question-search-multiple-languages.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-search-multiple-languages.txt	2019-05-22 15:20:07 +0000
@@ -12,7 +12,7 @@
 questions will be shown when the user has not provided language
 information.
 
-    >>> anon_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> anon_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> soup = find_main_content(anon_browser.contents)
     >>> for question in soup.findAll('td', 'questionTITLE'):
     ...     print(question.find('a').renderContents())
@@ -97,7 +97,7 @@
     >>> transaction.commit()
     >>> logout()
 
-    >>> anon_browser.open('http://launchpad.dev/kubuntu/+questions')
+    >>> anon_browser.open('http://launchpad.test/kubuntu/+questions')
     >>> anon_browser.getControl(name='field.language')
     Traceback (most recent call last):
       ...
@@ -115,7 +115,7 @@
 
     >>> anon_browser.addHeader('X_FORWARDED_FOR', '172.16.1.1')
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/+questions')
+    ...     'http://launchpad.test/ubuntu/+source/mozilla-firefox/+questions')
     >>> anon_browser.getControl(name='field.language')
     Traceback (most recent call last):
       ...
@@ -125,7 +125,7 @@
 then questions with those language will be displayed:
 
     >>> anon_browser.addHeader('Accept-Language', 'es, en')
-    >>> anon_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> anon_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> anon_browser.getControl('English (en)').selected
     True
     >>> anon_browser.getControl('Spanish (es)').selected
@@ -159,7 +159,7 @@
 rules. As with the anonymous user, the intersection of the GeoIP
 languages and the target's question languages is 'en'.
 
-    >>> user_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> user_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> sorted(user_browser.getControl(name='field.language').options)
     ['en']
 
@@ -168,7 +168,7 @@
 
     >>> user_browser.addHeader('X_FORWARDED_FOR', '172.16.1.1')
     >>> user_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/+questions')
+    ...     'http://launchpad.test/ubuntu/+source/mozilla-firefox/+questions')
     >>> user_browser.getControl(name='field.language')
     Traceback (most recent call last):
       ...
@@ -178,7 +178,7 @@
 are added to the language controls.
 
     >>> user_browser.addHeader('Accept-Language', 'es, en')
-    >>> user_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> user_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> sorted(user_browser.getControl(name='field.language').options)
     ['en', 'es']
 
@@ -192,7 +192,7 @@
     >>> from lp.testing.pages import strip_label
 
     >>> browser.addHeader('Authorization', 'Basic carlos@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> language_control = browser.getControl(name='field.language')
     >>> for label in sorted(language_control.displayOptions):
     ...     strip_label(label)
@@ -220,7 +220,7 @@
 searching Ubuntu.
 
     >>> daf_browser = setupBrowser(auth='Basic daf@xxxxxxxxxxxxx:test')
-    >>> daf_browser.open('http://launchpad.dev/~daf/+editlanguages')
+    >>> daf_browser.open('http://launchpad.test/~daf/+editlanguages')
     >>> daf_browser.getControl('English (United Kingdom)').selected
     True
     >>> daf_browser.getControl('Japanese').selected
@@ -235,7 +235,7 @@
 their languages. Daf, in this example, can see a language filter for
 English, and can use it to locate English questions.
 
-    >>> daf_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
+    >>> daf_browser.open('http://launchpad.test/distros/ubuntu/+questions')
     >>> language_control = daf_browser.getControl(name='field.language')
     >>> for label in language_control.displayOptions:
     ...     strip_label(label)
@@ -266,12 +266,12 @@
 but when they visit Mozilla Firefox, they are informed that there are
 questions going unanswered.
 
-    >>> user_browser.open('http://answers.launchpad.dev/kubuntu')
+    >>> user_browser.open('http://answers.launchpad.test/kubuntu')
     >>> paragraph = find_main_content(user_browser.contents).find('p')
     >>> print(extract_text(paragraph))
     There are no questions for Kubuntu with the requested statuses.
 
-    >>> user_browser.open('http://answers.launchpad.dev/firefox')
+    >>> user_browser.open('http://answers.launchpad.test/firefox')
     >>> paragraph = find_main_content(user_browser.contents).find('p')
     >>> print(extract_text(paragraph))
     Mozilla Firefox has unanswered questions in the following languages:
@@ -312,7 +312,7 @@
 
     >>> sample_person_browser = setupBrowser(
     ...     auth='Basic test@xxxxxxxxxxxxx:test')
-    >>> sample_person_browser.open('http://answers.launchpad.dev/ubuntu')
+    >>> sample_person_browser.open('http://answers.launchpad.test/ubuntu')
     >>> sample_person_browser.getLink('My questions').click()
     >>> sample_person_browser.getLink('Next').click()
     >>> print(sample_person_browser.title)

=== modified file 'lib/lp/answers/stories/question-subscriptions.txt'
--- lib/lp/answers/stories/question-subscriptions.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-subscriptions.txt	2019-05-22 15:20:07 +0000
@@ -11,13 +11,13 @@
 sees a link to their subscribed questions.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> user_browser.getLink('Subscribe').click()
     >>> print(user_browser.title)
     Subscription : Question #2 ...
 
     >>> print(user_browser.getLink('your subscribed questions page'))
-    <Link ...'http://answers.launchpad.dev/~no-priv/+subscribedquestions'>
+    <Link ...'http://answers.launchpad.test/~no-priv/+subscribedquestions'>
 
     >>> user_browser.getControl('Subscribe').click()
 
@@ -55,7 +55,7 @@
 on an existing question. The user can simply check the 'Email me future
 discussion about this question' checkbox:
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+question/6')
+    >>> user_browser.open('http://launchpad.test/firefox/+question/6')
     >>> user_browser.getControl('Message').value = (
     ...     "Try starting firefox from the command-line. Are there any "
     ...     "messages appearing?")

=== modified file 'lib/lp/answers/stories/question-workflow.txt'
--- lib/lp/answers/stories/question-workflow.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/question-workflow.txt	2019-05-22 15:20:07 +0000
@@ -33,7 +33,7 @@
 
 To participate in a question, the user must be logged in.
 
-    >>> anon_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> anon_browser.open('http://launchpad.test/firefox/+question/2')
     >>> print(anon_browser.contents)
     <!DOCTYPE...
     ...
@@ -55,7 +55,7 @@
 on the 'Add Information Request' button.
 
     >>> support_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> content = find_tag_by_id(
     ...     support_browser.contents, 'can-you-help-with-this-problem')
     >>> print(content.h2.renderContents())
@@ -122,7 +122,7 @@
 relevant to their tasks.
 
     >>> owner_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> content = find_tag_by_id(
     ...     owner_browser.contents, 'can-you-help-with-this-problem')
     >>> content is None
@@ -153,7 +153,7 @@
 clicking the 'Propose Answer' button.
 
     >>> support_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> support_browser.getControl('Message').value = (
     ...     "New version of the firefox package are available with SVG "
     ...     "support enabled. You can use apt-get or adept to upgrade.")
@@ -177,7 +177,7 @@
 'This Solved My Problem' button near the answer.
 
     >>> owner_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> soup = find_main_content(owner_browser.contents)
     >>> soup.findAll('div', 'boardComment')[-1].find('input', type='submit')
     <input type="submit" name="field.actions.confirm"
@@ -374,7 +374,7 @@
 The history of the question is available on the 'History' page.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/firefox/+question/2')
+    ...     'http://launchpad.test/firefox/+question/2')
     >>> anon_browser.getLink('History').click()
     >>> print(anon_browser.title)
     History of question #2...
@@ -420,7 +420,7 @@
 choosing an answer that helped him solve his problem.
 
     >>> carlos_browser = setupBrowser(auth='Basic carlos@xxxxxxxxxxxxx:test')
-    >>> carlos_browser.open('http://launchpad.dev/firefox/+question/12')
+    >>> carlos_browser.open('http://launchpad.test/firefox/+question/12')
     >>> print(find_request_status(carlos_browser.contents))
     Status: Open ...
 
@@ -453,7 +453,7 @@
 Firefox question. The solution does not work, but they think they have a
 similar problem so they ask their own question.
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
+    >>> user_browser.open('http://launchpad.test/firefox/+question/2')
 
     >>> content = find_main_content(user_browser.contents)
     >>> print(content.find(id='can-you-help-with-this-problem'))

=== modified file 'lib/lp/answers/stories/questions-index.txt'
--- lib/lp/answers/stories/questions-index.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/questions-index.txt	2019-05-22 15:20:07 +0000
@@ -16,7 +16,7 @@
     >>> logout()
     >>> transaction.commit()
 
-    >>> anon_browser.open('http://answers.launchpad.dev/')
+    >>> anon_browser.open('http://answers.launchpad.test/')
     >>> print(anon_browser.title)
     Launchpad Answers
 
@@ -71,7 +71,7 @@
     ...    ('firefox', 1)])
     >>> logout()
 
-    >>> anon_browser.open('http://answers.launchpad.dev/')
+    >>> anon_browser.open('http://answers.launchpad.test/')
     >>> print(extract_text(find_tag_by_id(
     ...     anon_browser.contents, 'most-active-projects')))
     Most active projects
@@ -83,6 +83,6 @@
 
     >>> anon_browser.getLink('Ubuntu').click()
     >>> print(anon_browser.url)
-    http://answers.launchpad.dev/ubuntu
+    http://answers.launchpad.test/ubuntu
     >>> print(anon_browser.title)
     Questions : Ubuntu

=== modified file 'lib/lp/answers/stories/this-is-a-faq.txt'
--- lib/lp/answers/stories/this-is-a-faq.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/this-is-a-faq.txt	2019-05-22 15:20:07 +0000
@@ -35,7 +35,7 @@
     # We use backslashreplace because the page title includes smart quotes.
     >>> from lp.services.helpers import backslashreplace
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+question/2')
+    ...     'http://answers.launchpad.test/firefox/+question/2')
     >>> print(backslashreplace(user_browser.title))
     Question #2 : ...
 
@@ -70,7 +70,7 @@
     ( ) 9: How do I troubleshoot problems with extensions/themes?
 
     >>> print(user_browser.getLink('How do I troubleshoot problems').url)
-    http://answers.launchpad.dev/firefox/+faq/9
+    http://answers.launchpad.test/firefox/+faq/9
 
 The query used to find these results is displayed in the search field
 under the radio widgets. That query defaults to the question's title.
@@ -113,7 +113,7 @@
 
     >>> user_browser.getControl('Link to FAQ').click()
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 They see that the question's status was changed to 'Answered':
 
@@ -130,7 +130,7 @@
     ...     find_tag_by_id(user_browser.contents, 'related-faq')))
     Related FAQ: How do I install plugins (Shockwave, QuickTime, etc.)? ...
     >>> print(user_browser.getLink('How do I install plugins').url)
-    http://answers.launchpad.dev/firefox/+faq/10
+    http://answers.launchpad.test/firefox/+faq/10
 
 The answer message was added to the question's discussion:
 
@@ -170,7 +170,7 @@
 with an error message.
 
     >>> print(user_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2/+linkfaq
+    http://answers.launchpad.test/firefox/+question/2/+linkfaq
     >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     You didn&#x27;t modify the linked FAQ.
@@ -217,7 +217,7 @@
     LinkNotFoundError
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+question/2/+createfaq')
+    ...     'http://answers.launchpad.test/firefox/+question/2/+createfaq')
     Traceback (most recent call last):
       ...
     Unauthorized: ...
@@ -226,13 +226,13 @@
 
     >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
     >>> owner_browser.open(
-    ...     'http://answers.launchpad.dev/firefox/+question/2')
+    ...     'http://answers.launchpad.test/firefox/+question/2')
     >>> owner_browser.getLink('Create a new FAQ')
     <Link text='Create a new FAQ' url='http://.../firefox/+question/2/+createfaq'>
     >>> owner_browser.getLink('Link to a FAQ').click()
     >>> owner_browser.getLink('create a new FAQ').click()
     >>> print(owner_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2/+createfaq
+    http://answers.launchpad.test/firefox/+question/2/+createfaq
     >>> print(owner_browser.title)
     Create a FAQ for Mozilla...
 
@@ -271,7 +271,7 @@
 
     >>> owner_browser.getControl('Create and Link').click()
     >>> print(owner_browser.url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 The answer message was added to the question's discussion:
 
@@ -296,7 +296,7 @@
 
     >>> owner_browser.getLink('Displaying SVG in Firefox').click()
     >>> print(owner_browser.url)
-    http://answers.launchpad.dev/firefox/+faq/...
+    http://answers.launchpad.test/firefox/+faq/...
     >>> print(backslashreplace(owner_browser.title))
     FAQ #... : Questions : Mozilla Firefox
 
@@ -325,7 +325,7 @@
     #2 SVG extension
 
     >>> print(owner_browser.getLink('SVG extension').url)
-    http://answers.launchpad.dev/firefox/+question/2
+    http://answers.launchpad.test/firefox/+question/2
 
 
 == Distribution and Source Packages ==
@@ -334,7 +334,7 @@
 can also be linked to FAQs.
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+question/11')
+    ...     'http://answers.launchpad.test/ubuntu/+question/11')
     >>> print(user_browser.title)
     Question #11 : ...
     >>> user_browser.getLink('Link to a FAQ').click()
@@ -342,7 +342,7 @@
     Is question #11 a FAQ...
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+source/mozilla-firefox'
+    ...     'http://answers.launchpad.test/ubuntu/+source/mozilla-firefox'
     ...     '/+question/8')
     >>> print(user_browser.title)
     Question #8 : ...
@@ -361,7 +361,7 @@
 information.
 
     >>> user_browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+source/mozilla-firefox/'
+    ...     'http://answers.launchpad.test/ubuntu/+source/mozilla-firefox/'
     ...     '+question/9')
     >>> details_portlet = find_portlet(
     ...     user_browser.contents, 'mozilla-firefox in ubuntu question #9')
@@ -405,14 +405,14 @@
 item, as above:
 
     >>> user_browser.getLink('FAQ #6').url
-    'http://answers.launchpad.dev/ubuntu/+faq/6'
+    'http://answers.launchpad.test/ubuntu/+faq/6'
 
 Or you can just refer to FAQs in comments:
 
     >>> user_browser.getControl('Message').value = 'No, this is FAQ #2'
     >>> user_browser.getControl('Just Add a Comment').click()
     >>> user_browser.getLink("FAQ #2").url
-    'http://answers.launchpad.dev/ubuntu/+faq/2'
+    'http://answers.launchpad.test/ubuntu/+faq/2'
 
 The linkification also happens, incidentally, in bug comments and
 anywhere else the email-to-html formatter is used. See

=== modified file 'lib/lp/answers/stories/webservice.txt'
--- lib/lp/answers/stories/webservice.txt	2018-06-02 19:27:16 +0000
+++ lib/lp/answers/stories/webservice.txt	2019-05-22 15:20:07 +0000
@@ -115,7 +115,7 @@
     ...     contact['self_link'], 'getDirectAnswerQuestionTargets',
     ...     api_version='devel').jsonBody()
     >>> print_self_link_of_entries(targets)
-    http://api.launchpad.dev/devel/my-project
+    http://api.launchpad.test/devel/my-project
 
 Anyone can retrieve the collection of `IQuestionTarget`s that a person's
 teams is an answer contact for using getTeamAnswerQuestionTargets.
@@ -124,7 +124,7 @@
     ...     contact['self_link'], 'getTeamAnswerQuestionTargets',
     ...     api_version='devel').jsonBody()
     >>> print_self_link_of_entries(targets)
-    http://api.launchpad.dev/devel/team-project
+    http://api.launchpad.test/devel/team-project
 
 
 Question collections
@@ -208,16 +208,16 @@
     date_solved: None
     description: u'description...'
     id: ...
-    language_link: u'http://api.launchpad.dev/devel/+languages/en'
+    language_link: u'http://api.launchpad.test/devel/+languages/en'
     messages_collection_link:
-        u'http://api.launchpad.dev/devel/my-project/+question/.../messages'
-    owner_link: u'http://api.launchpad.dev/devel/~asker'
-    resource_type_link: u'http://api.launchpad.dev/devel/#question'
-    self_link: u'http://api.launchpad.dev/devel/my-project/+question/...'
+        u'http://api.launchpad.test/devel/my-project/+question/.../messages'
+    owner_link: u'http://api.launchpad.test/devel/~asker'
+    resource_type_link: u'http://api.launchpad.test/devel/#question'
+    self_link: u'http://api.launchpad.test/devel/my-project/+question/...'
     status: u'Answered'
-    target_link: u'http://api.launchpad.dev/devel/my-project'
+    target_link: u'http://api.launchpad.test/devel/my-project'
     title: u'Q 1 great'
-    web_link: u'http://answers.launchpad.dev/my-project/+question/...'
+    web_link: u'http://answers.launchpad.test/my-project/+question/...'
 
 
 Question messages
@@ -236,13 +236,13 @@
     date_created: u'20...+00:00'
     index: 1
     new_status: u'Answered'
-    owner_link: u'http://api.launchpad.dev/devel/~contact'
+    owner_link: u'http://api.launchpad.test/devel/~contact'
     parent_link: None
-    question_link: u'http://api.launchpad.dev/devel/my-project/+question/...'
-    resource_type_link: u'http://api.launchpad.dev/devel/#question_message'
+    question_link: u'http://api.launchpad.test/devel/my-project/+question/...'
+    resource_type_link: u'http://api.launchpad.test/devel/#question_message'
     self_link:
-        u'http://api.launchpad.dev/devel/my-project/+question/.../messages/1'
+        u'http://api.launchpad.test/devel/my-project/+question/.../messages/1'
     subject: u'Re: Q 1 great'
     visible: True
     web_link:
-        u'http://answers.launchpad.dev/my-project/+question/.../messages/1'
+        u'http://answers.launchpad.test/my-project/+question/.../messages/1'

=== modified file 'lib/lp/app/browser/doc/base-layout.txt'
--- lib/lp/app/browser/doc/base-layout.txt	2018-05-23 14:35:48 +0000
+++ lib/lp/app/browser/doc/base-layout.txt	2019-05-22 15:20:07 +0000
@@ -16,7 +16,7 @@
 
     >>> user = factory.makePerson(name='waffles')
     >>> request = LaunchpadTestRequest(
-    ...     SERVER_URL='http://launchpad.dev',
+    ...     SERVER_URL='http://launchpad.test',
     ...     PATH_INFO='/~waffles/+layout')
     >>> request.setPrincipal(user)
 
@@ -135,18 +135,18 @@
     ...     """A simple view to test base-layout."""
     ...     __launchpad_facetname__ = 'bugs'
     >>> bugs_request = LaunchpadTestRequest(
-    ...     SERVER_URL='http://bugs.launchpad.dev',
+    ...     SERVER_URL='http://bugs.launchpad.test',
     ...     PATH_INFO='/~waffles/+layout')
     >>> bugs_request.setPrincipal(user)
     >>> view = BugsMainSideView(user, bugs_request)
     >>> footer = find_tag_by_id(html, 'footer')
     >>> for tag in footer.findAll('a'):
     ...     print tag.string, tag['href']
-    None http://launchpad.dev/
-    Take the tour http://launchpad.dev/+tour
+    None http://launchpad.test/
+    Take the tour http://launchpad.test/+tour
     Read the guide https://help.launchpad.net/
     Canonical&nbsp;Ltd. http://canonical.com/
-    Terms of use http://launchpad.dev/legal
+    Terms of use http://launchpad.test/legal
     Data privacy https://www.ubuntu.com/legal/dataprivacy
     Contact Launchpad Support /feedback
     Blog http://blog.launchpad.net/

=== modified file 'lib/lp/app/browser/doc/launchpad-search-pages.txt'
--- lib/lp/app/browser/doc/launchpad-search-pages.txt	2018-05-21 20:30:16 +0000
+++ lib/lp/app/browser/doc/launchpad-search-pages.txt	2019-05-22 15:20:07 +0000
@@ -39,7 +39,7 @@
     ...         search_param_list.append('%s=%s' % (name, value))
     ...     query_string = '&'.join(search_param_list)
     ...     request = LaunchpadTestRequest(
-    ...         SERVER_URL='https://launchpad.dev/+search',
+    ...         SERVER_URL='https://launchpad.test/+search',
     ...         QUERY_STRING=query_string, form=form, PATH_INFO='/+search')
     ...     search_view = getMultiAdapter((root, request), name="+search")
     ...     search_view.initialize()
@@ -514,7 +514,7 @@
     >>> page.title
     u'...Bug... #2 in Ubuntu Hoary: \u201cBlackhole Trash folder\u201d'
     >>> page.url
-    'http://bugs.launchpad.dev/ubuntu/hoary/+bug/2'
+    'http://bugs.launchpad.test/ubuntu/hoary/+bug/2'
     >>> page.summary
     u'...Launchpad\u2019s ...bug... tracker allows collaboration...'
 
@@ -566,7 +566,7 @@
 link to try the search again
 
     >>> print search_view.url
-    https://launchpad.dev/+search?field.text=gnomebaker
+    https://launchpad.test/+search?field.text=gnomebaker
 
 
 SearchFormView and SearchFormPrimaryView
@@ -589,7 +589,7 @@
     >>> search_form_view.id_suffix
     '-secondary'
     >>> print search_form_view.render()
-    <form action="http://launchpad.dev/+search"; method="get"
+    <form action="http://launchpad.test/+search"; method="get"
       accept-charset="UTF-8" id="sitesearch-secondary"
       name="sitesearch-secondary">
       <div>
@@ -611,7 +611,7 @@
     >>> search_form_view.id_suffix
     ''
     >>> print search_form_view.render()
-    <form action="http://launchpad.dev/+search"; method="get"
+    <form action="http://launchpad.test/+search"; method="get"
       accept-charset="UTF-8" id="sitesearch"
       name="sitesearch">
       <div>

=== modified file 'lib/lp/app/browser/doc/menu.txt'
--- lib/lp/app/browser/doc/menu.txt	2018-03-28 19:31:02 +0000
+++ lib/lp/app/browser/doc/menu.txt	2019-05-22 15:20:07 +0000
@@ -64,9 +64,9 @@
     Related pages
     >>> for link in menu_view.links:
     ...     print link.enabled, link.url
-    True   http://launchpad.dev/~beaker/+edit
-    True   http://launchpad.dev/~beaker/+edit-people
-    False  http://launchpad.dev/~beaker/+admin
+    True   http://launchpad.test/~beaker/+edit
+    True   http://launchpad.test/~beaker/+edit-people
+    False  http://launchpad.test/~beaker/+admin
 
 The view renders the heading using the menu title and a list of the links. A
 link is rendered only if its 'enabled' property is true. The template uses the
@@ -81,13 +81,13 @@
     <BLANKLINE>
         <li>
           <a class="menu-link-edit_thing sprite modify edit"
-             href="http://launchpad.dev/~beaker/+edit";>Edit thing</a>
+             href="http://launchpad.test/~beaker/+edit";>Edit thing</a>
         </li>
     <BLANKLINE>
     <BLANKLINE>
         <li>
             <a class="menu-link-edit_people"
-               href="http://launchpad.dev/~beaker/+edit-people";>Edit
+               href="http://launchpad.test/~beaker/+edit-people";>Edit
                  people related to thing</a>
         </li>
         ...
@@ -99,18 +99,18 @@
 be clickable.
 
     >>> request = LaunchpadTestRequest(
-    ...     SERVER_URL='http://launchpad.dev/~beaker/+edit')
+    ...     SERVER_URL='http://launchpad.test/~beaker/+edit')
     >>> print request.getURL()
-    http://launchpad.dev/~beaker/+edit
+    http://launchpad.test/~beaker/+edit
 
     >>> view = EditView(user, request)
     >>> menu_view = create_initialized_view(
     ...     view, '+related-pages', principal=user)
     >>> for link in menu_view.links:
     ...     print link.enabled, link.linked, link.url
-    True  False  http://launchpad.dev/~beaker/+edit
-    True  True   http://launchpad.dev/~beaker/+edit-people
-    False True   http://launchpad.dev/~beaker/+admin
+    True  False  http://launchpad.test/~beaker/+edit
+    True  True   http://launchpad.test/~beaker/+edit-people
+    False True   http://launchpad.test/~beaker/+admin
 
     >>> print menu_view.render()
     <div id="related-pages" class="portlet">
@@ -134,8 +134,8 @@
     ...     view, '+global-actions', principal=user)
     >>> for link in menu_view.enabled_links:
     ...     print link.enabled, link.linked, link.url
-    True  False  http://launchpad.dev/~beaker/+edit
-    True  True   http://launchpad.dev/~beaker/+edit-people
+    True  False  http://launchpad.test/~beaker/+edit
+    True  True   http://launchpad.test/~beaker/+edit-people
 
 The generated markup is for a portlet with the global-actions id.
 
@@ -148,7 +148,7 @@
         </li>
         <li>
           <a class="menu-link-edit_people"
-             href="http://launchpad.dev/~beaker/+edit-people";>Edit
+             href="http://launchpad.test/~beaker/+edit-people";>Edit
                people related to thing</a>
         </li>
       </ul>

=== modified file 'lib/lp/app/browser/launchpad.py'
--- lib/lp/app/browser/launchpad.py	2019-02-07 10:17:43 +0000
+++ lib/lp/app/browser/launchpad.py	2019-05-22 15:20:07 +0000
@@ -475,7 +475,7 @@
         http://launchpad.net/+main-template-macros
         http://launchpad.net/ubuntu/+main-template-macros
         http://launchpad.net/ubuntu/+main-template-macros
-        https://blueprints.launchpad.dev/ubuntu/hoary/+main-template-macros
+        https://blueprints.launchpad.test/ubuntu/hoary/+main-template-macros
 
     Obviously, those requests wouldn't do anything useful and would instead
     generate an OOPS.

=== modified file 'lib/lp/app/browser/tests/test_base_layout.py'
--- lib/lp/app/browser/tests/test_base_layout.py	2019-04-24 16:13:34 +0000
+++ lib/lp/app/browser/tests/test_base_layout.py	2019-05-22 15:20:07 +0000
@@ -56,7 +56,7 @@
             self.context = context
 
         request = LaunchpadTestRequest(
-            SERVER_URL='http://launchpad.dev', PATH_INFO='/~waffles/+layout')
+            SERVER_URL='http://launchpad.test', PATH_INFO='/~waffles/+layout')
         request.setPrincipal(self.user)
         request.traversed_objects.append(self.context)
         view = TemplateView(self.context, request)

=== modified file 'lib/lp/app/browser/tests/test_formatters.py'
--- lib/lp/app/browser/tests/test_formatters.py	2015-10-26 14:54:43 +0000
+++ lib/lp/app/browser/tests/test_formatters.py	2019-05-22 15:20:07 +0000
@@ -32,7 +32,7 @@
     def test_pagetitle_vhost(self):
         project = self.factory.makeProduct(name='fnord')
         view = create_view(project, name='+bugs', rootsite='bugs',
-            current_request=True, server_url='https://bugs.launchpad.dev/')
+            current_request=True, server_url='https://bugs.launchpad.test/')
         view.request.traversed_objects = [project, view]
         formatter = ObjectFormatterAPI(view)
         self.assertEqual('Bugs : Fnord', formatter.pagetitle())
@@ -62,7 +62,7 @@
         bug = self.factory.makeBug(target=project, title='bang')
         view = create_view(
             bug.bugtasks[0], name='+index', rootsite='bugs',
-            current_request=True, server_url='https://bugs.launchpad.dev/')
+            current_request=True, server_url='https://bugs.launchpad.test/')
         view.request.traversed_objects = [project, bug.bugtasks[0], view]
         formatter = ObjectFormatterAPI(view)
         self.assertEqual(
@@ -75,7 +75,7 @@
         bug = self.factory.makeBug(target=project, title=title)
         view = create_view(
             bug.bugtasks[0], name='+index', rootsite='bugs',
-            current_request=True, server_url='https://bugs.launchpad.dev/')
+            current_request=True, server_url='https://bugs.launchpad.test/')
         view.request.traversed_objects = [project, bug.bugtasks[0], view]
         formatter = ObjectFormatterAPI(view)
         detail = u'%s \u201c%s\u201d' % (bug.displayname, title)

=== modified file 'lib/lp/app/browser/tests/test_launchpad.py'
--- lib/lp/app/browser/tests/test_launchpad.py	2018-01-02 16:10:26 +0000
+++ lib/lp/app/browser/tests/test_launchpad.py	2019-05-22 15:20:07 +0000
@@ -56,7 +56,7 @@
 
 # We set the request header HTTP_REFERER  when we want to simulate navigation
 # from a valid page. This is used in the assertDisplaysNotification check.
-DEFAULT_REFERER = 'http://launchpad.dev'
+DEFAULT_REFERER = 'http://launchpad.test'
 
 
 class TraversalMixin:
@@ -123,7 +123,7 @@
 
         :param path: A slash-delimited path.
         :param use_default_referer: If True, set the referer attribute in the
-            request header to DEFAULT_REFERER = "http://launchpad.dev";
+            request header to DEFAULT_REFERER = "http://launchpad.test";
             (otherwise it remains as None)
         :return: The object found.
         """
@@ -667,7 +667,7 @@
         # Just /~/ expands to the current user.  (Bug 785800).
         person = self.factory.makePerson()
         login_person(person)
-        obj, view, req = test_traverse('http://launchpad.dev/~')
+        obj, view, req = test_traverse('http://launchpad.test/~')
         view = removeSecurityProxy(view)
         self.assertEqual(
             canonical_url(person),
@@ -676,13 +676,13 @@
     def test_self_url_not_logged_in(self):
         # /~/ when not logged in asks you to log in.
         self.assertRaises(Unauthorized,
-            test_traverse, 'http://launchpad.dev/~')
+            test_traverse, 'http://launchpad.test/~')
 
     def test_self_url_pathinfo(self):
         # You can traverse below /~/.
         person = self.factory.makePerson()
         login_person(person)
-        obj, view, req = test_traverse('http://launchpad.dev/~/+editsshkeys')
+        obj, view, req = test_traverse('http://launchpad.test/~/+editsshkeys')
         view = removeSecurityProxy(view)
         self.assertEqual(
             canonical_url(person) + '/+editsshkeys',
@@ -692,7 +692,7 @@
         # You can traverse below /~/.
         person = self.factory.makePerson()
         login_person(person)
-        obj, view, req = test_traverse('http://bugs.launchpad.dev/~')
+        obj, view, req = test_traverse('http://bugs.launchpad.test/~')
         view = removeSecurityProxy(view)
         self.assertEqual(
             canonical_url(person, rootsite='bugs'),

=== modified file 'lib/lp/app/browser/tests/test_launchpadroot.py'
--- lib/lp/app/browser/tests/test_launchpadroot.py	2019-04-24 16:13:34 +0000
+++ lib/lp/app/browser/tests/test_launchpadroot.py	2019-05-22 15:20:07 +0000
@@ -107,17 +107,17 @@
     def test_support(self):
         # The /support link redirects to answers.
         context, view, request = test_traverse(
-            'http://launchpad.dev/support')
+            'http://launchpad.test/support')
         view()
         self.assertEqual(301, request.response.getStatus())
         self.assertEqual(
-            'http://answers.launchpad.dev/launchpad',
+            'http://answers.launchpad.test/launchpad',
             request.response.getHeader('location'))
 
     def test_feedback(self):
         # The /feedback link redirects to the help site.
         context, view, request = test_traverse(
-            'http://launchpad.dev/feedback')
+            'http://launchpad.test/feedback')
         view()
         self.assertEqual(301, request.response.getStatus())
         self.assertEqual(

=== modified file 'lib/lp/app/browser/tests/test_macro_view.py'
--- lib/lp/app/browser/tests/test_macro_view.py	2017-07-21 09:41:18 +0000
+++ lib/lp/app/browser/tests/test_macro_view.py	2019-05-22 15:20:07 +0000
@@ -62,5 +62,5 @@
 
     def test_macro_names_not_traversable(self):
         for name in self.macro_names:
-            self.assertTrue(self.is_not_found('http://launchpad.dev/' + name),
+            self.assertTrue(self.is_not_found('http://launchpad.test/' + name),
                 'macro name %r should not be URL accessable' % name)

=== modified file 'lib/lp/app/browser/tests/test_page_macro.py'
--- lib/lp/app/browser/tests/test_page_macro.py	2015-07-08 16:05:11 +0000
+++ lib/lp/app/browser/tests/test_page_macro.py	2019-05-22 15:20:07 +0000
@@ -69,7 +69,7 @@
         self._setUpView()
 
     def test_base_template(self):
-        # Requests on the launchpad.dev vhost use the Launchpad base template.
+        # Requests on the launchpad.test vhost use the Launchpad base template.
         adapter = self.getAdapter([self.view], IPathAdapter, name='macro')
         template_path = os.path.normpath(adapter.base.filename)
         self.assertIn('lp/app/templates', template_path)

=== modified file 'lib/lp/app/browser/tests/test_vocabulary.py'
--- lib/lp/app/browser/tests/test_vocabulary.py	2016-02-05 16:51:12 +0000
+++ lib/lp/app/browser/tests/test_vocabulary.py	2019-05-22 15:20:07 +0000
@@ -143,7 +143,7 @@
         getUtility(IIrcIDSet).new(person, 'eg.dom', 'snarf')
         getUtility(IIrcIDSet).new(person, 'ex.dom', 'pting')
         entry = get_picker_entry(person, None, picker_expander_enabled=True)
-        self.assertEqual('http://launchpad.dev/~snarf', entry.alt_title_link)
+        self.assertEqual('http://launchpad.test/~snarf', entry.alt_title_link)
         self.assertEqual(
             ['snarf on eg.dom, pting on ex.dom', 'Member since 2005-01-30'],
             entry.details)
@@ -152,7 +152,7 @@
         # The person picker provides more information for teams.
         team = self.factory.makeTeam(email='fnord@xxxxxx', name='fnord')
         entry = get_picker_entry(team, None, picker_expander_enabled=True)
-        self.assertEqual('http://launchpad.dev/~fnord', entry.alt_title_link)
+        self.assertEqual('http://launchpad.test/~fnord', entry.alt_title_link)
         self.assertEqual(['Team members: 1'], entry.details)
 
     def test_PersonPickerEntryAdapter_badges(self):
@@ -256,7 +256,7 @@
             sourcepackagename='snarf', distroseries=series, publish=True)
         dsp = distro.getSourcePackage('snarf')
         self.assertEqual(
-            'http://launchpad.dev/fnord/+source/snarf',
+            'http://launchpad.test/fnord/+source/snarf',
             self.getPickerEntry(dsp).alt_title_link)
 
 
@@ -309,7 +309,7 @@
     def test_product_provides_alt_title_link(self):
         product = self.factory.makeProduct(name='fnord')
         self.assertEqual(
-            'http://launchpad.dev/fnord',
+            'http://launchpad.test/fnord',
             self.getPickerEntry(product).alt_title_link)
 
     def test_provides_commercial_subscription_none(self):
@@ -388,7 +388,7 @@
     def test_projectgroup_provides_alt_title_link(self):
         projectgroup = self.factory.makeProject(name='fnord')
         self.assertEqual(
-            'http://launchpad.dev/fnord',
+            'http://launchpad.test/fnord',
             self.getPickerEntry(projectgroup).alt_title_link)
 
 
@@ -445,7 +445,7 @@
     def test_distribution_provides_alt_title_link(self):
         distribution = self.factory.makeDistribution(name='fnord')
         self.assertEqual(
-            'http://launchpad.dev/fnord',
+            'http://launchpad.test/fnord',
             self.getPickerEntry(distribution).alt_title_link)
 
 
@@ -544,7 +544,7 @@
         result = simplejson.loads(view())
         expected = [{
             "alt_title": team.name,
-            "alt_title_link": "http://launchpad.dev/~%s"; % team.name,
+            "alt_title_link": "http://launchpad.test/~%s"; % team.name,
             "api_uri": "/~%s" % team.name,
             "badges":
                 [{"label": product.displayname,
@@ -562,7 +562,7 @@
             },
             {
             "alt_title": person.name,
-            "alt_title_link": "http://launchpad.dev/~%s"; % person.name,
+            "alt_title_link": "http://launchpad.test/~%s"; % person.name,
             "api_uri": "/~%s" % person.name,
             "css": "sprite person",
             "description": "<email address hidden>",

=== modified file 'lib/lp/app/doc/displaying-paragraphs-of-text.txt'
--- lib/lp/app/doc/displaying-paragraphs-of-text.txt	2016-06-20 22:52:44 +0000
+++ lib/lp/app/doc/displaying-paragraphs-of-text.txt	2019-05-22 15:20:07 +0000
@@ -332,12 +332,12 @@
     ...     'faq  number  2\n'
     ...     )
     >>> print test_tales('foo/fmt:text-to-html', foo=text)
-    <p><a href="http://answers.launchpad.dev/ubuntu/+faq/1";>faq 1</a><br />
-    <a href="http://answers.launchpad.dev/ubuntu/+faq/2";>faq #2</a><br />
-    <a href="http://answers.launchpad.dev/ubuntu/+faq/2";>faq-2</a><br />
-    <a href="http://answers.launchpad.dev/ubuntu/+faq/2";>faq=2</a><br />
-    <a href="http://answers.launchpad.dev/ubuntu/+faq/1";>faq item 1</a><br />
-    <a href="http://answers.launchpad.dev/ubuntu/+faq/2";>faq number 2</a></p>
+    <p><a href="http://answers.launchpad.test/ubuntu/+faq/1";>faq 1</a><br />
+    <a href="http://answers.launchpad.test/ubuntu/+faq/2";>faq #2</a><br />
+    <a href="http://answers.launchpad.test/ubuntu/+faq/2";>faq-2</a><br />
+    <a href="http://answers.launchpad.test/ubuntu/+faq/2";>faq=2</a><br />
+    <a href="http://answers.launchpad.test/ubuntu/+faq/1";>faq item 1</a><br />
+    <a href="http://answers.launchpad.test/ubuntu/+faq/2";>faq number 2</a></p>
 
 Except, that is, when the FAQ doesn't exist:
 

=== modified file 'lib/lp/app/doc/hierarchical-menu.txt'
--- lib/lp/app/doc/hierarchical-menu.txt	2017-10-21 18:14:14 +0000
+++ lib/lp/app/doc/hierarchical-menu.txt	2019-05-22 15:20:07 +0000
@@ -74,7 +74,7 @@
 
     >>> from lp.testing.menu import make_fake_request
     >>> request = make_fake_request(
-    ...     'http://launchpad.dev/joy-of-cooking/spam',
+    ...     'http://launchpad.test/joy-of-cooking/spam',
     ...     [root, cookbook, recipe])
 
 The hierarchy's list of breadcrumbs is empty since none of the objects
@@ -113,10 +113,10 @@
     >>> hierarchy = getMultiAdapter((recipe, request), name='+hierarchy')
     >>> hierarchy.items
     [<TextualBreadcrumb
-        url='http://launchpad.dev/joy-of-cooking'
+        url='http://launchpad.test/joy-of-cooking'
         text='Joy of cooking'>,
      <TextualBreadcrumb
-        url='http://launchpad.dev/joy-of-cooking/spam'
+        url='http://launchpad.test/joy-of-cooking/spam'
         text='Spam'>]
 
 The ICooker object contains a path prefix, a segment of the path that
@@ -124,7 +124,7 @@
 domains. The `Hierarchy` model copes fine with objects like that.
 
     >>> cooker_request = make_fake_request(
-    ...     'http://launchpad.dev/+cooker/jamie',
+    ...     'http://launchpad.test/+cooker/jamie',
     ...     [root, cooker])
 
     >>> provideAdapter(TextualBreadcrumb, [ICooker], IBreadcrumb)
@@ -172,7 +172,7 @@
 
     >>> ham_recipe = Recipe('ham', cookbook)
     >>> ham_request = make_fake_request(
-    ...     'http://launchpad.dev/joy-of-cooking/ham',
+    ...     'http://launchpad.test/joy-of-cooking/ham',
     ...     [root, cookbook, ham_recipe])
 
     >>> ham_hierarchy = getMultiAdapter(
@@ -203,7 +203,7 @@
     >>> print breadcrumb.text
     None
     >>> print breadcrumb.url
-    http://launchpad.dev/joy-of-cooking
+    http://launchpad.test/joy-of-cooking
 
 As said above, the breadcrumb's attributes can be overridden with subclassing
 and Python properties.
@@ -216,7 +216,7 @@
     >>> breadcrumb = DynamicBreadcrumb(cookbook)
     >>> breadcrumb
     <DynamicBreadcrumb
-        url='http://launchpad.dev/joy-of-cooking'
+        url='http://launchpad.test/joy-of-cooking'
         text='Joy of cooking'>
 
 
@@ -238,7 +238,7 @@
     >>> spammy_hierarchy = CustomHierarchy(root, request)
     >>> spammy_hierarchy.items
     [<TextualBreadcrumb
-        url='http://launchpad.dev/joy-of-cooking/spam'
+        url='http://launchpad.test/joy-of-cooking/spam'
         text='Spam'>]
 
 
@@ -270,7 +270,7 @@
     >>> print markup
     <ol itemprop="breadcrumb" class="breadcrumbs">
       <li>
-        <a href="http://launchpad.dev/joy-of-cooking";>Joy of cooking</a>
+        <a href="http://launchpad.test/joy-of-cooking";>Joy of cooking</a>
       </li>
       <li>
           Spam
@@ -281,7 +281,7 @@
 considered to be on the home page if there are no breadcrumbs.
 
     # Simulate a visit to the site root
-    >>> request = make_fake_request('http://launchpad.dev/', [root])
+    >>> request = make_fake_request('http://launchpad.test/', [root])
     >>> homepage_hierarchy = getMultiAdapter(
     ...     (root, request), name='+hierarchy')
 

=== modified file 'lib/lp/app/doc/launchpadform.txt'
--- lib/lp/app/doc/launchpadform.txt	2018-07-15 16:23:15 +0000
+++ lib/lp/app/doc/launchpadform.txt	2019-05-22 15:20:07 +0000
@@ -356,7 +356,7 @@
   ...
   ...     @action(u'Redirect', name='redirect')
   ...     def redirect_action(self, action, data):
-  ...         self.next_url = 'http://launchpad.dev/'
+  ...         self.next_url = 'http://launchpad.test/'
   ...
   ...     def handleUpdateFailure(self, action, data, errors):
   ...         return u'Some errors occured.'

=== modified file 'lib/lp/app/doc/lazr-js-widgets.txt'
--- lib/lp/app/doc/lazr-js-widgets.txt	2017-12-24 15:45:26 +0000
+++ lib/lp/app/doc/lazr-js-widgets.txt	2019-05-22 15:20:07 +0000
@@ -55,7 +55,7 @@
         Widgets &gt; important
     </span>
         <a class="yui3-editable_text-trigger sprite edit action-icon"
-           href="http://launchpad.dev/widget/+edit";
+           href="http://launchpad.test/widget/+edit";
            title="">Edit</a>
     </h1>
     <script>
@@ -89,20 +89,20 @@
   * provide an 'edit_title' to set the title attribute of the anchor
 
     >>> print widget.edit_url
-    http://launchpad.dev/widget/+edit
+    http://launchpad.test/widget/+edit
 
     >>> diff_view = TextLineEditorWidget(
     ...     product, title_field, title, 'h1', edit_view='+edit-people',
     ...     edit_title='Change the product title')
     >>> print diff_view.edit_url
-    http://launchpad.dev/widget/+edit-people
+    http://launchpad.test/widget/+edit-people
     >>> print diff_view.edit_title
     Change the product title
     >>> ignored = login_person(product.owner)
     >>> print diff_view()
     <h1...
     <a class="yui3-editable_text-trigger sprite edit action-icon"
-       href="http://launchpad.dev/widget/+edit-people";
+       href="http://launchpad.test/widget/+edit-people";
        title="Change the product title">...
 
     >>> diff_url = TextLineEditorWidget(
@@ -174,7 +174,7 @@
         <div class="clearfix">
           <div class="edit-controls">
             <a class="yui3-editable_text-trigger sprite edit action-icon"
-               href="http://launchpad.dev/~eric/+archive/ubuntu/ppa/+edit";
+               href="http://launchpad.test/~eric/+archive/ubuntu/ppa/+edit";
                title="">Edit</a>
           </div>
           <h3>A title</h3>
@@ -272,7 +272,7 @@
       </span>
       <span>
         <a class="sprite edit action-icon lazr-btn yui3-activator-act"
-          href="http://launchpad.dev/widget/+edit";
+          href="http://launchpad.test/widget/+edit";
           title="">Edit</a>
         <div class="yui3-activator-message-box yui3-activator-hidden"></div>
     </span> ...
@@ -340,7 +340,7 @@
     My email: <span class="value">Don't hide it</span>
       <span>
         <a class="editicon sprite edit action-icon"
-           href="http://launchpad.dev/~eric/+edit"; title="">Edit</a>
+           href="http://launchpad.test/~eric/+edit"; title="">Edit</a>
       </span>
     </span>
     <script>
@@ -417,7 +417,7 @@
       <dt>
         Recipe distro series
           <a class="sprite edit action-icon lazr-btn yui3-activator-act"
-             href="http://code.launchpad.dev/~eric/+recipe/cake_recipe/+edit";
+             href="http://code.launchpad.test/~eric/+recipe/cake_recipe/+edit";
              id="edit-distroseries-btn"
              title="">Edit</a>
       </dt>

=== modified file 'lib/lp/app/doc/menus.txt'
--- lib/lp/app/doc/menus.txt	2017-12-24 15:45:26 +0000
+++ lib/lp/app/doc/menus.txt	2019-05-22 15:20:07 +0000
@@ -86,14 +86,14 @@
     >>> recipe = Recipe('fried-spam', cookbook)
 
     >>> request = make_fake_request(
-    ...     'http://launchpad.dev/joy-of-cooking/fried-spam',
+    ...     'http://launchpad.test/joy-of-cooking/fried-spam',
     ...     traversed_objects=[cookbook, recipe])
 
     >>> canonical_url(cookbook)
-    u'http://launchpad.dev/joy-of-cooking'
+    u'http://launchpad.test/joy-of-cooking'
 
     >>> canonical_url(recipe)
-    u'http://launchpad.dev/joy-of-cooking/fried-spam'
+    u'http://launchpad.test/joy-of-cooking/fried-spam'
 
 Content objects are not suitable for presentation by themselves; they
 require a view class to adapt them to the required format. An object may
@@ -302,22 +302,22 @@
 
     >>> summarise_links(
     ...     CookeryFacetMenu(cookbook),
-    ...     url='http://launchpad.dev/joy-of-cooking',
+    ...     url='http://launchpad.test/joy-of-cooking',
     ...     facet=None)
     link summary
-        url: http://launchpad.dev/joy-of-cooking
+        url: http://launchpad.test/joy-of-cooking
         enabled: True
         menu: None
         selected: True
         linked: False
     link questions
-        url: http://launchpad.dev/joy-of-cooking/+questions
+        url: http://launchpad.test/joy-of-cooking/+questions
         enabled: True
         menu: None
         selected: False
         linked: True
     link variations
-        url: http://launchpad.dev/joy-of-cooking/+variations
+        url: http://launchpad.test/joy-of-cooking/+variations
         enabled: False
         menu: None
         selected: False
@@ -441,14 +441,14 @@
 
     >>> summarise_links(
     ...     RecipeMenu(recipe),
-    ...     url='http://launchpad.dev/joy-of-cooking/fried-spam')
+    ...     url='http://launchpad.test/joy-of-cooking/fried-spam')
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: False
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: True
@@ -481,22 +481,22 @@
 
     >>> summarise_links(
     ...     RecipeFacetMenu(recipe),
-    ...     url='http://launchpad.dev/joy-of-cooking/fried-spam',
+    ...     url='http://launchpad.test/joy-of-cooking/fried-spam',
     ...     facet=None)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: None
         selected: True
         linked: False
     link questions
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+questions
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+questions
         enabled: True
         menu: None
         selected: False
         linked: True
     link variations
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+variations
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+variations
         enabled: True
         menu: None
         selected: False
@@ -545,7 +545,7 @@
 
     >>> summarise_links(
     ...     BogusFacetMenu(recipe),
-    ...     url='http://launchpad.dev/joy-of-cooking/fried-spam',
+    ...     url='http://launchpad.test/joy-of-cooking/fried-spam',
     ...     facet=None)
     Traceback (most recent call last):
     ...
@@ -577,7 +577,7 @@
     ...     IFacetMenu, INavigationMenu)
 
     >>> request = make_fake_request(
-    ...     'http://launchpad.dev/joy-of-cooking/fried-spam',
+    ...     'http://launchpad.test/joy-of-cooking/fried-spam',
     ...     traversed_objects=[cookbook, recipe])
     >>> recipe_view = getMultiAdapter((recipe, request), name='+index')
     >>> request._last_obj_traversed = recipe_view
@@ -637,16 +637,16 @@
     >>> recipe_navigationmenu = queryAdapter(
     ...     recipe, INavigationMenu, name='cookery')
     >>> request.getURL()
-    'http://launchpad.dev/joy-of-cooking/fried-spam'
+    'http://launchpad.test/joy-of-cooking/fried-spam'
 
     >>> summarise_links(recipe_navigationmenu)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: False
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: True
@@ -656,14 +656,14 @@
 
     >>> summarise_links(
     ...     recipe_navigationmenu,
-    ...     url='http://launchpad.dev/joy-of-cooking/fried-spam/+journal')
+    ...     url='http://launchpad.test/joy-of-cooking/fried-spam/+journal')
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: True
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: False
@@ -672,13 +672,13 @@
 
     >>> summarise_links(
     ...     recipe_navigationmenu,
-    ...     url='http://launchpad.dev/joy-of-cooking/fried-spam?x=1')
+    ...     url='http://launchpad.test/joy-of-cooking/fried-spam?x=1')
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         ...
         linked: False
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         ...
         linked: True
 
@@ -687,7 +687,7 @@
 
     >>> summarise_links(
     ...     recipe_questions_navigationmenu,
-    ...     url=('http://launchpad.dev/joy-of-cooking/fried-spam/+questions'
+    ...     url=('http://launchpad.test/joy-of-cooking/fried-spam/+questions'
     ...          '?filter=all&sort=Descending'))
     link all_questions
         url: http://.../joy-of-cooking/fried-spam/+questions?filter=all
@@ -700,7 +700,7 @@
 
     >>> summarise_links(
     ...     recipe_questions_navigationmenu,
-    ...     url=('http://launchpad.dev/joy-of-cooking/fried-spam/+questions'
+    ...     url=('http://launchpad.test/joy-of-cooking/fried-spam/+questions'
     ...          '?filter=Obsolete'))
     link all_questions
         url: http://.../joy-of-cooking/fried-spam/+questions?filter=all
@@ -724,25 +724,25 @@
 linked, as can be seen when the url is not explicitly passed.
 
     >>> request.getURL()
-    'http://launchpad.dev/joy-of-cooking/fried-spam'
+    'http://launchpad.test/joy-of-cooking/fried-spam'
 
     >>> summarise_links(
     ...     CookeryFacetMenu(cookbook),
     ...     facet='questions')
     link summary
-        url: http://launchpad.dev/joy-of-cooking
+        url: http://launchpad.test/joy-of-cooking
         enabled: True
         menu: None
         selected: False
         linked: True
     link questions
-        url: http://launchpad.dev/joy-of-cooking/+questions
+        url: http://launchpad.test/joy-of-cooking/+questions
         enabled: True
         menu: None
         selected: True
         linked: True
     link variations
-        url: http://launchpad.dev/joy-of-cooking/+variations
+        url: http://launchpad.test/joy-of-cooking/+variations
         enabled: False
         menu: None
         selected: False
@@ -763,7 +763,7 @@
 identical to the one in the parent's menu.)
 
     >>> request = make_fake_request(
-    ...     'http://launchpad.dev'
+    ...     'http://launchpad.test'
     ...     '/joy-of-cooking/fried-spam/+edit-ingredients',
     ...     traversed_objects=[cookbook, recipe])
     >>> recipe_ingredients_view = getMultiAdapter(
@@ -774,12 +774,12 @@
     ...     recipe, INavigationMenu, name='cookery')
     >>> summarise_links(recipe_summary_menu)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: False
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: True
@@ -788,12 +788,12 @@
     ...     recipe_ingredients_view, INavigationMenu, name='cookery')
     >>> summarise_links(recipe_overview_menu)
     link edit_instructions
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+edit-instructions
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+edit-instructions
         enabled: True
         menu: None
         linked: True
     link edit_ingredients
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+edit-ingredients
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+edit-ingredients
         enabled: True
         menu: None
         linked: False
@@ -803,7 +803,7 @@
 menu of the RecipeMenu will change the state of both menus.
 
     >>> request = make_fake_request(
-    ...     'http://launchpad.dev/joy-of-cooking/fried-spam/+read-journal',
+    ...     'http://launchpad.test/joy-of-cooking/fried-spam/+read-journal',
     ...     traversed_objects=[cookbook, recipe])
     >>> recipe_journal_view = getMultiAdapter(
     ...     (recipe, request), name='+read-journal')
@@ -811,12 +811,12 @@
 
     >>> summarise_links(recipe_summary_menu)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: True
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: False
@@ -825,12 +825,12 @@
     ...     queryAdapter(
     ...         recipe_journal_view, INavigationMenu, name='cookery'))
     link read_journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+read-journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+read-journal
         enabled: True
         menu: None
         linked: False
     link write_entry
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+write-entry
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+write-entry
         enabled: True
         menu: None
         linked: True
@@ -867,7 +867,7 @@
     ...         return Link(target, text)
     ...
     ...     def baz(self):
-    ...         target = 'http://launchpad.dev/joy-of-cooking/+baz'
+    ...         target = 'http://launchpad.test/joy-of-cooking/+baz'
     ...         text = 'Baz'
     ...         return Link(target, text)
     ...
@@ -877,17 +877,17 @@
     ...         return Link(target, text)
 
     >>> print canonical_url(cookbook)
-    http://launchpad.dev/joy-of-cooking
+    http://launchpad.test/joy-of-cooking
 
-    >>> request_url = URI('http://launchpad.dev/joy-of-cooking')
+    >>> request_url = URI('http://launchpad.test/joy-of-cooking')
 
     >>> facets = AbsoluteUrlTargetTestFacets(cookbook)
     >>> for link in facets.iterlinks(request_url):
     ...     print link.url, link.linked
-    http://launchpad.dev/joy-of-cooking False
+    http://launchpad.test/joy-of-cooking False
     ftp://barlink.example.com/barbarbar True
-    http://launchpad.dev/joy-of-cooking/+baz True
-    http://launchpad.dev/joy-of-cooking/+spoo True
+    http://launchpad.test/joy-of-cooking/+baz True
+    http://launchpad.test/joy-of-cooking/+spoo True
 
 
 The current view's menu
@@ -908,12 +908,12 @@
 
     >>> summarise_links(recipe_summary_menu)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: True
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: False
@@ -949,19 +949,19 @@
     ...     'context/menu:facet', context=recipe, request=request)
     >>> summarise_tal_links(links_list)
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: None
         selected: True
         linked: True
     link questions
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+questions
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+questions
         enabled: True
         menu: None
         selected: False
         linked: True
     link variations
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+variations
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+variations
         enabled: True
         menu: None
         selected: False
@@ -971,12 +971,12 @@
     ...     'context/menu:navigation', context=recipe, request=request)
     >>> summarise_tal_links(links_dict)
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: False
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: True
@@ -987,12 +987,12 @@
     >>> summarise_tal_links(links_dict)
     attribute journal_entries: 42
     link read_journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+read-journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+read-journal
         enabled: True
         menu: None
         linked: False
     link write_entry
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+write-entry
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+write-entry
         enabled: True
         menu: None
         linked: True
@@ -1036,7 +1036,7 @@
 
     >>> comment = Comment('a-comment', recipe)
     >>> print canonical_url(comment)
-    http://launchpad.dev/joy-of-cooking/fried-spam/a-comment
+    http://launchpad.test/joy-of-cooking/fried-spam/a-comment
 
 When we try to look up the menu for the comment, the navigation menu for
 the next highest object in the URL hierarchy, the Recipe, will be
@@ -1046,12 +1046,12 @@
     ...     'context/menu:navigation', context=comment)
     >>> summarise_tal_links(links_dict)
     link journal
-        url: http://launchpad.dev/joy-of-cooking/fried-spam/+journal
+        url: http://launchpad.test/joy-of-cooking/fried-spam/+journal
         enabled: True
         menu: <...IRecipeJournalMenuMarker...>
         linked: False
     link summary
-        url: http://launchpad.dev/joy-of-cooking/fried-spam
+        url: http://launchpad.test/joy-of-cooking/fried-spam
         enabled: True
         menu: <...IRecipeEditMenuMarker...>
         linked: True
@@ -1188,7 +1188,7 @@
 URL.
 
     >>> request.getURL()
-    'http://launchpad.dev/joy-of-cooking/fried-spam/+read-journal'
+    'http://launchpad.test/joy-of-cooking/fried-spam/+read-journal'
 
     >>> recipe_view_menu_view = NavigationMenuView(
     ...     recipe_journal_view, request)

=== modified file 'lib/lp/app/doc/tales.txt'
--- lib/lp/app/doc/tales.txt	2017-12-24 15:45:26 +0000
+++ lib/lp/app/doc/tales.txt	2019-05-22 15:20:07 +0000
@@ -325,7 +325,7 @@
 The `fmt:url` is used when you want the canonical URL of a given object.
 
     >>> print test_tales("bug/fmt:url", bug=bug)
-    http://bugs.launchpad.dev/bugs/1
+    http://bugs.launchpad.test/bugs/1
 
 You can also specify an extra argument (a view's name), if you want the
 URL of a given page under that object. For that to work, though, we need
@@ -334,7 +334,7 @@
     >>> from lp.services.webapp.servers import LaunchpadTestRequest
     >>> login(ANONYMOUS, LaunchpadTestRequest())
     >>> print test_tales("bug/fmt:url/+text", bug=bug)
-    http://bugs.launchpad.dev/bugs/1/+text
+    http://bugs.launchpad.test/bugs/1/+text
 
 
 fmt:url accepts an rootsite extension to make URLs to a specific application.
@@ -343,16 +343,16 @@
     ...     LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
 
     >>> print test_tales("person/fmt:url:bugs", person=mark)
-    http://bugs.launchpad.dev/~mark
+    http://bugs.launchpad.test/~mark
 
     >>> print test_tales("person/fmt:url:feeds", person=mark)
-    http://feeds.launchpad.dev/~mark
+    http://feeds.launchpad.test/~mark
 
     >>> print test_tales("pillar/fmt:url:answers", pillar=ubuntu)
-    http://answers.launchpad.dev/ubuntu
+    http://answers.launchpad.test/ubuntu
 
     >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
-    http://launchpad.dev/bugs/1
+    http://launchpad.test/bugs/1
 
     >>> login(ANONYMOUS)
 
@@ -434,28 +434,28 @@
     ...     LaunchpadTestRequest(SERVER_URL='http://code.launchpad.net'))
 
     >>> print test_tales("pillar/fmt:link:translations", pillar=ubuntu)
-    <a ...http://translations.launchpad.dev/ubuntu...
+    <a ...http://translations.launchpad.test/ubuntu...
 
     >>> print test_tales("person/fmt:url:feeds", person=mark)
-    http://feeds.launchpad.dev/~mark
+    http://feeds.launchpad.test/~mark
 
     >>> print test_tales("bug/fmt:url:mainsite", bug=bug)
-    http://launchpad.dev/bugs/1
+    http://launchpad.test/bugs/1
 
 The default behaviour for pillars, persons, and teams is to link to
 the mainsite.
 
     >>> print test_tales("pillar/fmt:link", pillar=ubuntu)
-    <a ...http://launchpad.dev/ubuntu...
+    <a ...http://launchpad.test/ubuntu...
 
     >>> print test_tales("person/fmt:link", person=mark)
-    <a ...http://launchpad.dev/~mark...
+    <a ...http://launchpad.test/~mark...
 
     >>> print test_tales("person/fmt:link-display-name-id", person=mark)
-    <a ...http://launchpad.dev/~mark...>Mark Shuttleworth (mark)</a>
+    <a ...http://launchpad.test/~mark...>Mark Shuttleworth (mark)</a>
 
     >>> print test_tales("team/fmt:link", team=ubuntu_team)
-    <a ...http://launchpad.dev/~ubuntu-team...
+    <a ...http://launchpad.test/~ubuntu-team...
 
     >>> login(ANONYMOUS)
 
@@ -514,7 +514,7 @@
 The bzr-link formatter uses the bzr identity.
 
     >>> print test_tales("branch/fmt:bzr-link", branch=branch)
-    <a href="http://code.launchpad.dev/~eric/fooix/bar";
+    <a href="http://code.launchpad.test/~eric/fooix/bar";
       class="sprite branch">lp://dev/~eric/fooix/bar</a>
 
     >>> ignored = login_person(fooix.owner, LaunchpadTestRequest())
@@ -694,7 +694,7 @@
 The url for the release file can be retrieved using fmt:url.
 
     >>> print test_tales("release_file/fmt:url", release_file=release_file)
-    http://launchpad.dev/.../+download/test.txt
+    http://launchpad.test/.../+download/test.txt
 
 HTML in the file description is escaped in the fmt:link.
 
@@ -703,7 +703,7 @@
     >>> print test_tales("release_file/fmt:link", release_file=release_file)
     <img ...
     <a title="&gt;&lt;script&gt;XSS failed&lt;/script&gt; (4 bytes)"
-    href="http://launchpad.dev/.../+download/test.txt";>test.txt</a> ...
+    href="http://launchpad.test/.../+download/test.txt";>test.txt</a> ...
 
 
 
@@ -788,10 +788,10 @@
 The "standard" 'url' name is supported:
 
     >>> test_tales("bugtracker/fmt:url", bugtracker=bugtracker)
-    u'http://bugs.launchpad.dev/bugs/bugtrackers/email'
+    u'http://bugs.launchpad.test/bugs/bugtrackers/email'
 
 (The url is relative if possible, and our test request claims to be from
-launchpad.dev, so the url is relative.)
+launchpad.test, so the url is relative.)
 
 As are 'link', 'external-link', 'external-title-link' and 'aliases',
 which help when hiding email addresses from users who are not logged in.
@@ -851,10 +851,10 @@
 The "standard" 'url' name is supported:
 
     >>> test_tales("bugwatch/fmt:url", bugwatch=sf_bugwatch)
-    u'http://bugs.launchpad.dev/bugs/12/+watch/...'
+    u'http://bugs.launchpad.test/bugs/12/+watch/...'
 
     >>> test_tales("bugwatch/fmt:url", bugwatch=email_bugwatch)
-    u'http://bugs.launchpad.dev/bugs/12/+watch/...'
+    u'http://bugs.launchpad.test/bugs/12/+watch/...'
 
 As are 'external-link' and 'external-link-short', which help when hiding
 email addresses from users who are not logged in:
@@ -1200,22 +1200,22 @@
 
     >>> test_tales('foo/fmt:linkify-email',
     ...    foo='I am the mighty foo.bar@xxxxxxxxxxxxx hear me roar.')
-    u'...<a href="http://launchpad.dev/~name16";
+    u'...<a href="http://launchpad.test/~name16";
       class="sprite person">foo.bar@xxxxxxxxxxxxx</a>...'
 
 Multiple addresses may be linkified at once:
 
     >>> test_tales('foo/fmt:linkify-email',
     ...     foo='foo.bar@xxxxxxxxxxxxx and cprov@xxxxxxxxxx')
-    u'<a href="http://launchpad.dev/~name16";
+    u'<a href="http://launchpad.test/~name16";
       class="sprite person">foo.bar@xxxxxxxxxxxxx</a>
-      and <a href="http://launchpad.dev/~cprov";
+      and <a href="http://launchpad.test/~cprov";
         class="sprite person">cprov@xxxxxxxxxx</a>'
 
 Team addresses are linkified with a team icon:
 
     >>> test_tales('foo/fmt:linkify-email', foo='support@xxxxxxxxxx')
-    u'<a href="http://launchpad.dev/~ubuntu-team";
+    u'<a href="http://launchpad.test/~ubuntu-team";
       class="sprite team">support@xxxxxxxxxx</a>'
 
 Unknown email addresses are not altered in any way:
@@ -1495,14 +1495,14 @@
     >>> login(ANONYMOUS, request)
     >>> link = Link('+place', 'text', 'summary', icon='icon', enabled=True)
     >>> menu_link = MenuLink(link)
-    >>> menu_link.url = "http://launchpad.dev/+place";
+    >>> menu_link.url = "http://launchpad.test/+place";
     >>> menu_link.name = 'test_link'
 
 The link can be rendered as an anchored icon.
 
     >>> print test_tales('menu_link/fmt:icon', menu_link=menu_link)
     <a class="menu-link-test_link sprite icon action-icon"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
 The default rendering can be explicitly called too, text with an icon to
@@ -1510,7 +1510,7 @@
 
     >>> print test_tales('menu_link/fmt:link', menu_link=menu_link)
     <a class="menu-link-test_link sprite icon"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
 The 'edit', 'remove' and 'trash-icon' links are rendered icons followed
@@ -1519,19 +1519,19 @@
     >>> menu_link.icon = 'edit'
     >>> print test_tales('menu_link/fmt:link', menu_link=menu_link)
     <a class="menu-link-test_link sprite modify edit"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
     >>> menu_link.icon = 'remove'
     >>> print test_tales('menu_link/fmt:link', menu_link=menu_link)
     <a class="menu-link-test_link sprite modify remove"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
     >>> menu_link.icon = 'trash-icon'
     >>> print test_tales('menu_link/fmt:link', menu_link=menu_link)
     <a class="menu-link-test_link sprite modify trash-icon"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
 fmt:icon-link and fmt:link-icon are deprecated. They are an alias for
@@ -1541,18 +1541,18 @@
     >>> menu_link.icon = 'icon'
     >>> print test_tales('menu_link/fmt:icon-link', menu_link=menu_link)
     <a class="menu-link-test_link sprite icon"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
     >>> print test_tales('menu_link/fmt:link-icon', menu_link=menu_link)
     <a class="menu-link-test_link sprite icon"
-       href="http://launchpad.dev/+place";
+       href="http://launchpad.test/+place";
        title="summary">text</a>
 
 And the url format is also available.
 
     >>> print test_tales('menu_link/fmt:url', menu_link=menu_link)
-    http://launchpad.dev/+place
+    http://launchpad.test/+place
 
 If the link is disabled, no markup is rendered.
 

=== modified file 'lib/lp/app/doc/textformatting.txt'
--- lib/lp/app/doc/textformatting.txt	2011-12-23 22:27:40 +0000
+++ lib/lp/app/doc/textformatting.txt	2019-05-22 15:20:07 +0000
@@ -258,7 +258,7 @@
     ...     date='today',
     ...     message_id='<aardvark>',
     ...     # And this is the one we're really interested in.
-    ...     review_url=('http://launchpad.dev/~frankly-my-dear-i-'
+    ...     review_url=('http://launchpad.test/~frankly-my-dear-i-'
     ...                 'dont-give-a-damn/+review-moderation-messages'),
     ...     )
 
@@ -283,7 +283,7 @@
     <BLANKLINE>
     To review all messages pending approval, visit:
     <BLANKLINE>
-        http://launchpad.dev/~frankly-my-dear-i-dont-give-a-damn/+review-
+        http://launchpad.test/~frankly-my-dear-i-dont-give-a-damn/+review-
     moderation-messages
     <BLANKLINE>
     Regards,
@@ -316,7 +316,7 @@
     <BLANKLINE>
     To review all messages pending approval, visit:
     <BLANKLINE>
-        http://launchpad.dev/~frankly-my-dear-i-dont-give-a-damn/+review-moderation-messages
+        http://launchpad.test/~frankly-my-dear-i-dont-give-a-damn/+review-moderation-messages
     <BLANKLINE>
     Regards,
     The Launchpad team

=== modified file 'lib/lp/app/javascript/subscribers/subscribers_list.js'
--- lib/lp/app/javascript/subscribers/subscribers_list.js	2017-09-22 19:41:16 +0000
+++ lib/lp/app/javascript/subscribers/subscribers_list.js	2019-05-22 15:20:07 +0000
@@ -244,7 +244,7 @@
  *           "display_name": "Foo Bar",
  *           "can_edit": true/false,
  *           "is_team": true/false,
- *           "web_link": "https://launchpad.dev/~foobar";,
+ *           "web_link": "https://launchpad.test/~foobar";,
  *           "display_subscribed_by": "Matt Zimmerman (mdz)"
  *           },
  *         "subscription_level": "Details"},

=== modified file 'lib/lp/app/javascript/testing/tests/test_mockio.js'
--- lib/lp/app/javascript/testing/tests/test_mockio.js	2017-07-24 15:37:03 +0000
+++ lib/lp/app/javascript/testing/tests/test_mockio.js	2019-05-22 15:20:07 +0000
@@ -21,7 +21,7 @@
     tests.suite.add(new Y.Test.Case({
         name: 'mockio_tests',
 
-        test_url: "https://launchpad.dev/test/url";,
+        test_url: "https://launchpad.test/test/url";,
 
         setUp: function() {
             // Initialize call_count on recorders.

=== modified file 'lib/lp/app/javascript/tests/test_hide_comment.js'
--- lib/lp/app/javascript/tests/test_hide_comment.js	2014-05-28 20:38:48 +0000
+++ lib/lp/app/javascript/tests/test_hide_comment.js	2019-05-22 15:20:07 +0000
@@ -29,7 +29,7 @@
                     config.on.success();
                 };
             LP.cache.comment_context = {
-                self_link: 'https://launchpad.dev/api/devel/some/comment/'
+                self_link: 'https://launchpad.test/api/devel/some/comment/'
             };
             this.comment_list = new Y.lp.app.comment.CommentList();
             this.comment_list.render();
@@ -47,7 +47,7 @@
             Y.Assert.areEqual('Unhide', link.get('text'),
                 'Link text should be \'Unhide\'');
             Y.Assert.areEqual(
-                'https://launchpad.dev/api/devel/some/comment/',
+                'https://launchpad.test/api/devel/some/comment/',
                 LP.cache.call_data.called_url, 'Call with wrong url.');
             Y.Assert.areEqual(
                 'setCommentVisibility', LP.cache.call_data.called_func,
@@ -67,7 +67,7 @@
             Y.Assert.areEqual('Hide', link.get('text'),
                 'Link text should be \'Hide\'');
             Y.Assert.areEqual(
-                'https://launchpad.dev/api/devel/some/comment/',
+                'https://launchpad.test/api/devel/some/comment/',
                 LP.cache.call_data.called_url, 'Call with wrong url.');
             Y.Assert.areEqual(
                 'setCommentVisibility', LP.cache.call_data.called_func,

=== modified file 'lib/lp/app/javascript/tests/test_lp.js'
--- lib/lp/app/javascript/tests/test_lp.js	2012-10-26 10:00:20 +0000
+++ lib/lp/app/javascript/tests/test_lp.js	2019-05-22 15:20:07 +0000
@@ -8,17 +8,17 @@
 
         test_get_url_path_with_pillar: function () {
             Y.Assert.areEqual(
-                '/fnord', Y.lp.get_url_path('http://launchpad.dev/fnord'));
+                '/fnord', Y.lp.get_url_path('http://launchpad.test/fnord'));
         },
 
         test_get_url_path_without_slash: function () {
             Y.Assert.areEqual(
-                '/', Y.lp.get_url_path('http://launchpad.dev'));
+                '/', Y.lp.get_url_path('http://launchpad.test'));
         },
 
         test_get_url_path_with_double_slash: function () {
             Y.Assert.areEqual(
-                '/fnord', Y.lp.get_url_path('http://launchpad.dev//fnord'));
+                '/fnord', Y.lp.get_url_path('http://launchpad.test//fnord'));
         }
     }));
 

=== modified file 'lib/lp/app/stories/basics/copyright.txt'
--- lib/lp/app/stories/basics/copyright.txt	2019-01-05 09:54:44 +0000
+++ lib/lp/app/stories/basics/copyright.txt	2019-05-22 15:20:07 +0000
@@ -2,14 +2,14 @@
 
 The tour pages.
 
-  >>> browser.open('http://launchpad.dev/')
+  >>> browser.open('http://launchpad.test/')
   >>> browser.getLink('Take the tour').click()
   >>> print extract_text(find_tag_by_id(browser.contents, 'footer-navigation'))
   Next...&copy; 2004-2019 Canonical Ltd...
 
 The main template.
 
-  >>> browser.open('http://launchpad.dev')
+  >>> browser.open('http://launchpad.test')
   >>> print extract_text(find_tag_by_id(browser.contents, 'footer'))
   &copy; 2004-2019 Canonical Ltd.
   ...

=== modified file 'lib/lp/app/stories/basics/demo-and-lpnet.txt'
--- lib/lp/app/stories/basics/demo-and-lpnet.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/stories/basics/demo-and-lpnet.txt	2019-05-22 15:20:07 +0000
@@ -38,7 +38,7 @@
 
 For a 3-0 page:
 
-    >>> browser.open('http://launchpad.dev/ubuntu')
+    >>> browser.open('http://launchpad.test/ubuntu')
     >>> print browser.contents
     <...
     <style...url(/@@/demo)...</style>
@@ -60,7 +60,7 @@
 
 First for a 3-0 page:
 
-    >>> browser.open('http://launchpad.dev/ubuntu')
+    >>> browser.open('http://launchpad.test/ubuntu')
     >>> print extract_text(find_tag_by_id(browser.contents, 'lp-version'))
     &bull; r... devmode (Get the code!)
     >>> len(find_tags_by_class(browser.contents, 'sitemessage'))

=== modified file 'lib/lp/app/stories/basics/marketing.txt'
--- lib/lp/app/stories/basics/marketing.txt	2011-05-27 18:27:59 +0000
+++ lib/lp/app/stories/basics/marketing.txt	2019-05-22 15:20:07 +0000
@@ -2,15 +2,15 @@
 
 From Launchpad's front page, you can access the tour.
 
-    >>> browser.open('http://launchpad.dev/')
+    >>> browser.open('http://launchpad.test/')
     >>> tour_link = browser.getLink('Take the tour')
     >>> print tour_link.url
-    http://launchpad.dev/+tour
+    http://launchpad.test/+tour
     >>> tour_link.click()
     >>> print browser.title
     Launchpad tour
     >>> print browser.url
-    http://launchpad.dev/+tour/index
+    http://launchpad.test/+tour/index
 
 The tour is circular. Clicking the Next button repeatedly will bring you
 back to the tour start.
@@ -18,32 +18,32 @@
     >>> def take_the_tour(steps_taken=0):
     ...     browser.getLink(id='btnNext').click()
     ...     print browser.url
-    ...     if browser.url != 'http://launchpad.dev/+tour/index':
+    ...     if browser.url != 'http://launchpad.test/+tour/index':
     ...         if steps_taken >= 20:
     ...             raise RuntimeError('Never ending tour!')
     ...         take_the_tour(steps_taken=steps_taken+1)
     >>> take_the_tour()
-    http://launchpad.dev/+tour/bugs
-    http://launchpad.dev/+tour/branch-hosting-tracking
-    http://launchpad.dev/+tour/translation
-    http://launchpad.dev/+tour/community
-    http://launchpad.dev/+tour/ppa
-    http://launchpad.dev/+tour/community-support
-    http://launchpad.dev/+tour/api
-    http://launchpad.dev/+tour/feature-tracking
-    http://launchpad.dev/+tour/release-management
-    http://launchpad.dev/+tour/join-launchpad
-    http://launchpad.dev/+tour/index
+    http://launchpad.test/+tour/bugs
+    http://launchpad.test/+tour/branch-hosting-tracking
+    http://launchpad.test/+tour/translation
+    http://launchpad.test/+tour/community
+    http://launchpad.test/+tour/ppa
+    http://launchpad.test/+tour/community-support
+    http://launchpad.test/+tour/api
+    http://launchpad.test/+tour/feature-tracking
+    http://launchpad.test/+tour/release-management
+    http://launchpad.test/+tour/join-launchpad
+    http://launchpad.test/+tour/index
 
 The images from the tour are retrieved relative to +tour.
 
-    >>> browser.open('http://launchpad.dev/+tour/images/home/main-image.jpg')
-    >>> browser.open('http://launchpad.dev/+tour/images/btn-next.png')
+    >>> browser.open('http://launchpad.test/+tour/images/home/main-image.jpg')
+    >>> browser.open('http://launchpad.test/+tour/images/btn-next.png')
 
 But the source directory isn't available:
 
     >>> browser.open(
-    ...     'http://launchpad.dev/+tour/source/code-hosting_SVG.svg')
+    ...     'http://launchpad.test/+tour/source/code-hosting_SVG.svg')
     Traceback (most recent call last):
       ...
     NotFound:...
@@ -54,29 +54,29 @@
 Each application used to have an introduction living at +about, this is
 now redirected to the relevant tour page.
 
-    >>> browser.open('http://launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/index
-
-    >>> browser.open('http://code.launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/branch-hosting-tracking
-
-    >>> browser.open('http://bugs.launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/bugs
-
-    >>> browser.open('http://blueprints.launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/feature-tracking
-
-    >>> browser.open('http://translations.launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/translation
-
-    >>> browser.open('http://answers.launchpad.dev/+about')
-    >>> print browser.url
-    http://launchpad.dev/+tour/community-support
+    >>> browser.open('http://launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/index
+
+    >>> browser.open('http://code.launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/branch-hosting-tracking
+
+    >>> browser.open('http://bugs.launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/bugs
+
+    >>> browser.open('http://blueprints.launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/feature-tracking
+
+    >>> browser.open('http://translations.launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/translation
+
+    >>> browser.open('http://answers.launchpad.test/+about')
+    >>> print browser.url
+    http://launchpad.test/+tour/community-support
 
 
 == +tour compatibility ==
@@ -84,29 +84,29 @@
 Similarly, each application has their +tour redirecting to their proper
 tour page.
 
-    >>> browser.open('http://launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/index
-
-    >>> browser.open('http://code.launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/branch-hosting-tracking
-
-    >>> browser.open('http://bugs.launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/bugs
-
-    >>> browser.open('http://blueprints.launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/feature-tracking
-
-    >>> browser.open('http://translations.launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/translation
-
-    >>> browser.open('http://answers.launchpad.dev/+tour')
-    >>> print browser.url
-    http://launchpad.dev/+tour/community-support
+    >>> browser.open('http://launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/index
+
+    >>> browser.open('http://code.launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/branch-hosting-tracking
+
+    >>> browser.open('http://bugs.launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/bugs
+
+    >>> browser.open('http://blueprints.launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/feature-tracking
+
+    >>> browser.open('http://translations.launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/translation
+
+    >>> browser.open('http://answers.launchpad.test/+tour')
+    >>> print browser.url
+    http://launchpad.test/+tour/community-support
 
 
 == +faq compatibility ==
@@ -114,25 +114,25 @@
 Each application also had a +faq link, that link is also redirected to
 the appropriate tour page.
 
-    >>> browser.open('http://code.launchpad.dev/+faq')
-    >>> print browser.url
-    http://launchpad.dev/+tour/branch-hosting-tracking
-
-    >>> browser.open('http://bugs.launchpad.dev/+faq')
-    >>> print browser.url
-    http://launchpad.dev/+tour/bugs
-
-    >>> browser.open('http://blueprints.launchpad.dev/+faq')
-    >>> print browser.url
-    http://launchpad.dev/+tour/feature-tracking
-
-    >>> browser.open('http://translations.launchpad.dev/+faq')
-    >>> print browser.url
-    http://launchpad.dev/+tour/translation
-
-    >>> browser.open('http://answers.launchpad.dev/+faq')
-    >>> print browser.url
-    http://launchpad.dev/+tour/community-support
+    >>> browser.open('http://code.launchpad.test/+faq')
+    >>> print browser.url
+    http://launchpad.test/+tour/branch-hosting-tracking
+
+    >>> browser.open('http://bugs.launchpad.test/+faq')
+    >>> print browser.url
+    http://launchpad.test/+tour/bugs
+
+    >>> browser.open('http://blueprints.launchpad.test/+faq')
+    >>> print browser.url
+    http://launchpad.test/+tour/feature-tracking
+
+    >>> browser.open('http://translations.launchpad.test/+faq')
+    >>> print browser.url
+    http://launchpad.test/+tour/translation
+
+    >>> browser.open('http://answers.launchpad.test/+faq')
+    >>> print browser.url
+    http://launchpad.test/+tour/community-support
 
 
 == Links to tour on application main page ==
@@ -143,44 +143,44 @@
 
 === Code ===
 
-    >>> browser.open('http://code.launchpad.dev')
+    >>> browser.open('http://code.launchpad.test')
     >>> tour_link = browser.getLink('Take a tour')
     >>> print tour_link.url
-    http://launchpad.dev/+tour/branch-hosting-tracking
+    http://launchpad.test/+tour/branch-hosting-tracking
     >>> tour_link.click()
 
 
 === Bugs ===
 
-    >>> browser.open('http://bugs.launchpad.dev')
+    >>> browser.open('http://bugs.launchpad.test')
     >>> tour_link = browser.getLink('take a tour')
     >>> print tour_link.url
-    http://bugs.launchpad.dev/+tour
+    http://bugs.launchpad.test/+tour
     >>> tour_link.click()
 
 
 === Blueprints ===
 
-    >>> browser.open('http://blueprints.launchpad.dev')
+    >>> browser.open('http://blueprints.launchpad.test')
     >>> tour_link = browser.getLink('Take a tour')
     >>> print tour_link.url
-    http://launchpad.dev/+tour/feature-tracking
+    http://launchpad.test/+tour/feature-tracking
     >>> tour_link.click()
 
 
 === Translations ===
 
-    >>> browser.open('http://translations.launchpad.dev')
+    >>> browser.open('http://translations.launchpad.test')
     >>> tour_link = browser.getLink('Take a tour')
     >>> print tour_link.url
-    http://launchpad.dev/+tour/translation
+    http://launchpad.test/+tour/translation
     >>> tour_link.click()
 
 
 === Answers ===
 
-    >>> browser.open('http://answers.launchpad.dev')
+    >>> browser.open('http://answers.launchpad.test')
     >>> tour_link = browser.getLink('Take a tour')
     >>> print tour_link.url
-    http://launchpad.dev/+tour/community-support
+    http://launchpad.test/+tour/community-support
     >>> tour_link.click()

=== modified file 'lib/lp/app/stories/basics/max-batch-size.txt'
--- lib/lp/app/stories/basics/max-batch-size.txt	2011-05-27 18:27:59 +0000
+++ lib/lp/app/stories/basics/max-batch-size.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 
     >>> from zope.testbrowser.testing import Browser
     >>> anon_browser = Browser()
-    >>> anon_browser.open('http://launchpad.dev/projects/+all?start=0&batch=1000')
+    >>> anon_browser.open('http://launchpad.test/projects/+all?start=0&batch=1000')
     Traceback (most recent call last):
     ...
     HTTPError: HTTP Error 400: Bad Request

=== modified file 'lib/lp/app/stories/basics/notfound-error.txt'
--- lib/lp/app/stories/basics/notfound-error.txt	2011-09-26 06:30:07 +0000
+++ lib/lp/app/stories/basics/notfound-error.txt	2019-05-22 15:20:07 +0000
@@ -9,11 +9,11 @@
 
     >>> page_with_referer = str(http(r"""
     ... GET /+fhqwhgads HTTP/1.1
-    ... Referer: http://launchpad.dev/+about
+    ... Referer: http://launchpad.test/+about
     ... """))
     >>> print page_with_referer
     HTTP/1.1 404 Not Found
-    ...href="http://launchpad.dev/+about";...
+    ...href="http://launchpad.test/+about";...
 
 It also contains instructions specific to broken links.
 

=== modified file 'lib/lp/app/stories/basics/notfound-traversals.txt'
--- lib/lp/app/stories/basics/notfound-traversals.txt	2016-06-22 03:48:31 +0000
+++ lib/lp/app/stories/basics/notfound-traversals.txt	2019-05-22 15:20:07 +0000
@@ -4,14 +4,14 @@
 instead of a correct 404 page. So we test them to ensure the correct
 HTTP status is returned.
 
-    >>> def check_not_found(url, host='launchpad.dev'):
+    >>> def check_not_found(url, host='launchpad.test'):
     ...     output = http("GET %s HTTP/1.1\nHost: %s" % (url, host))
     ...     status = output.getStatus()
     ...     if status != 404:
     ...         return "%s returned status %s instead of 404\n\n%s" % (
     ...             url, status, str(output))
 
-    >>> def check_redirect(url, auth=False, host='launchpad.dev', status=303):
+    >>> def check_redirect(url, auth=False, host='launchpad.test', status=303):
     ...     get_cmd = """
     ... GET %s HTTP/1.1
     ... Host: %s
@@ -29,8 +29,8 @@
     >>> check_redirect("/feedback", status=301)
     >>> check_redirect("/support/", status=301)
 
-    >>> check_redirect("/", host='feeds.launchpad.dev', status=301)
-    >>> check_redirect("/+index", host='feeds.launchpad.dev', status=301)
+    >>> check_redirect("/", host='feeds.launchpad.test', status=301)
+    >>> check_redirect("/+index", host='feeds.launchpad.test', status=301)
 
 The +translate page in the main host is obsolete so it's now a redirect
 to the translations site. This way, we don't break existing links to it.

=== modified file 'lib/lp/app/stories/basics/page-request-summaries.txt'
--- lib/lp/app/stories/basics/page-request-summaries.txt	2011-12-19 13:20:13 +0000
+++ lib/lp/app/stories/basics/page-request-summaries.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 finish rendering -- the authoritative source is in OOPS reports or in the web
 app's stderr.
 
-  >>> browser.open('http://launchpad.dev/')
+  >>> browser.open('http://launchpad.test/')
   >>> print browser.contents
   <!DOCTYPE...
   ...<!--... At least ... actions issued in ... seconds ...-->...
@@ -12,7 +12,7 @@
 It's available for any page:
 
 
-  >>> browser.open('http://launchpad.dev/~mark/')
+  >>> browser.open('http://launchpad.test/~mark/')
   >>> print browser.contents
   <!DOCTYPE...
   ...<!--... At least ... actions issued in ... seconds ...-->...

=== modified file 'lib/lp/app/stories/basics/user-requested-oops.txt'
--- lib/lp/app/stories/basics/user-requested-oops.txt	2011-12-19 13:20:13 +0000
+++ lib/lp/app/stories/basics/user-requested-oops.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 A user can request an oops for a page by using the ++oops++ namespace in any
 page traversal.
 
-    >>> browser.open("http://launchpad.dev/++oops++";)
+    >>> browser.open("http://launchpad.test/++oops++";)
 
 The OOPS id is put into the comment at the end of the document.
 
@@ -24,7 +24,7 @@
 
 The ++oops++ can be anywhere in the traversal.
 
-    >>> browser.open("http://launchpad.dev/gnome-terminal/++oops++/trunk";)
+    >>> browser.open("http://launchpad.test/gnome-terminal/++oops++/trunk";)
     >>> (page, summary) = browser.contents.split('</body>')
     >>> print summary
     ...

=== modified file 'lib/lp/app/stories/basics/xx-dbpolicy.txt'
--- lib/lp/app/stories/basics/xx-dbpolicy.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/app/stories/basics/xx-dbpolicy.txt	2019-05-22 15:20:07 +0000
@@ -52,7 +52,7 @@
 Read only requests such as GET and HEAD will use the MAIN SLAVE
 Store by default.
 
-    >>> browser.open('http://launchpad.dev/+whichdb')
+    >>> browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(browser)
     SLAVE
 
@@ -67,7 +67,7 @@
 special dispensation has been made. Without a session, subsequent requests
 will then immediately return to using the SLAVE.
 
-    >>> browser.open('http://launchpad.dev/+whichdb')
+    >>> browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(browser)
     SLAVE
 
@@ -82,17 +82,17 @@
     >>> browser.getControl('Do Post').click() # POST request
     >>> print whichdb(browser)
     MASTER
-    >>> browser.open('http://launchpad.dev/+whichdb') # GET request
+    >>> browser.open('http://launchpad.test/+whichdb') # GET request
     >>> print whichdb(browser)
     MASTER
 
 GET and HEAD requests from other clients are unaffected though
 and use the MAIN SLAVE Store by default.
 
-    >>> anon_browser.open('http://launchpad.dev/+whichdb')
+    >>> anon_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(anon_browser)
     SLAVE
-    >>> admin_browser.open('http://launchpad.dev/+whichdb')
+    >>> admin_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(admin_browser)
     SLAVE
 
@@ -110,13 +110,13 @@
     ...     return _original_now() + timedelta(minutes=10)
 
 
-    >>> browser.open('http://launchpad.dev/+whichdb')
+    >>> browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(browser)
     MASTER
 
     >>> dbpolicy._now = _future_now # Install the time machine.
 
-    >>> browser.open('http://launchpad.dev/+whichdb')
+    >>> browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(browser)
     SLAVE
 
@@ -127,12 +127,12 @@
 replication oddities from becoming too bad, as well as lightening the load
 on the slaves allowing them to catch up.
 
-    >>> anon_browser.open('http://launchpad.dev/+whichdb')
+    >>> anon_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(anon_browser)
     SLAVE
 
     >>> dbpolicy._test_lag = timedelta(minutes=10)
-    >>> anon_browser.open('http://launchpad.dev/+whichdb')
+    >>> anon_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(anon_browser)
     MASTER
     >>> dbpolicy._test_lag = None
@@ -148,24 +148,24 @@
     >>> anon_browser.raiseHttpErrors = False
 
     # Confirm requests are going to the SLAVE
-    >>> anon_browser.open('http://launchpad.dev/+whichdb')
+    >>> anon_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(anon_browser)
     SLAVE
 
     # The slave database contains no data, but we don't get
     # a 404 page - the request is retried against the MASTER.
-    >>> anon_browser.open('http://launchpad.dev/~stub')
+    >>> anon_browser.open('http://launchpad.test/~stub')
     >>> anon_browser.headers['Status']
     '200 Ok'
 
     # 404s are still returned though if the data doesn't exist in the
     # MASTER database either.
-    >>> anon_browser.open('http://launchpad.dev/~does-not-exist')
+    >>> anon_browser.open('http://launchpad.test/~does-not-exist')
     >>> anon_browser.headers['Status']
     '404 Not Found'
 
     # This session is still using the SLAVE though by default.
-    >>> anon_browser.open('http://launchpad.dev/+whichdb')
+    >>> anon_browser.open('http://launchpad.test/+whichdb')
     >>> print whichdb(anon_browser)
     SLAVE
 

=== modified file 'lib/lp/app/stories/basics/xx-launchpad-statistics.txt'
--- lib/lp/app/stories/basics/xx-launchpad-statistics.txt	2011-12-19 13:20:13 +0000
+++ lib/lp/app/stories/basics/xx-launchpad-statistics.txt	2019-05-22 15:20:07 +0000
@@ -2,7 +2,7 @@
 We also have the special Launchpad Statistics summary page. This is only
 acessible to launchpad Admins:
 
-  >>> user_browser.open('http://launchpad.dev/+statistics')
+  >>> user_browser.open('http://launchpad.test/+statistics')
   Traceback (most recent call last):
     ...
   Unauthorized:...
@@ -10,7 +10,7 @@
 
 When we login as an admin, we can see all the stats listed:
 
-  >>> admin_browser.open('http://launchpad.dev/+statistics/')
+  >>> admin_browser.open('http://launchpad.test/+statistics/')
   >>> print admin_browser.title
   Launchpad statistics
   >>> 'answered_question_count' in admin_browser.contents

=== modified file 'lib/lp/app/stories/basics/xx-lowercase-redirection.txt'
--- lib/lp/app/stories/basics/xx-lowercase-redirection.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/app/stories/basics/xx-lowercase-redirection.txt	2019-05-22 15:20:07 +0000
@@ -3,34 +3,34 @@
 When someone visits a page such as http://launchpad.net/jOkOshEr
 launchpad does a permanent redirect to the lowercase path:
 
-    >>> anon_browser.open('http://launchpad.dev/jOkOshEr')
+    >>> anon_browser.open('http://launchpad.test/jOkOshEr')
     >>> print anon_browser.url
-    http://launchpad.dev/jokosher
+    http://launchpad.test/jokosher
 
 The redirection also works for URLs below the root, and with query
 parameters:
 
-    >>> anon_browser.open('http://launchpad.dev/UbUntU/+search?text=foo')
+    >>> anon_browser.open('http://launchpad.test/UbUntU/+search?text=foo')
     >>> print anon_browser.url
-    http://launchpad.dev/ubuntu/+search?text=foo
+    http://launchpad.test/ubuntu/+search?text=foo
 
 The redirection also works for other Launchpad subdomains:
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/jOkOshEr/+bugs?orderby=-datecreated')
+    ...     'http://bugs.launchpad.test/jOkOshEr/+bugs?orderby=-datecreated')
     >>> print anon_browser.url
-    http://bugs.launchpad.dev/jokosher/+bugs?orderby=-datecreated
+    http://bugs.launchpad.test/jokosher/+bugs?orderby=-datecreated
 
-    >>> anon_browser.open('http://answers.launchpad.dev/~nAmE12')
+    >>> anon_browser.open('http://answers.launchpad.test/~nAmE12')
     >>> print anon_browser.url
-    http://answers.launchpad.dev/~name12
+    http://answers.launchpad.test/~name12
     
 When doing a POST to an invalid URL, we get an error:
 
     >>> print http(r"""
     ... POST /UbUntU/hoary/+source/evolution/+pots/evolution-2.2/es/+translate HTTP/1.1
-    ... Host: translations.launchpad.dev
-    ... Referer: https://launchpad.dev/
+    ... Host: translations.launchpad.test
+    ... Referer: https://launchpad.test/
     ... """)
     HTTP/1.1 500 Internal Server Error
     ...

=== modified file 'lib/lp/app/stories/basics/xx-offsite-form-post.txt'
--- lib/lp/app/stories/basics/xx-offsite-form-post.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/app/stories/basics/xx-offsite-form-post.txt	2019-05-22 15:20:07 +0000
@@ -21,7 +21,7 @@
 "evil.people.com", the post fails:
 
   >>> browser = setupBrowserWithReferrer('http://evil.people.com/')
-  >>> browser.open('http://launchpad.dev/people/+newteam')
+  >>> browser.open('http://launchpad.test/people/+newteam')
   >>> browser.getControl('Name', index=0).value = 'team1'
   >>> browser.getControl('Display Name').value = 'Team 1'
   >>> browser.getControl('Create').click()
@@ -33,7 +33,7 @@
 Similarly, posting with a garbage referer fails:
 
   >>> browser = setupBrowserWithReferrer('not a url')
-  >>> browser.open('http://launchpad.dev/people/+newteam')
+  >>> browser.open('http://launchpad.test/people/+newteam')
   >>> browser.getControl('Name', index=0).value = 'team2'
   >>> browser.getControl('Display Name').value = 'Team 2'
   >>> browser.getControl('Create').click()
@@ -45,7 +45,7 @@
 It also fails if there is no referrer.
 
   >>> browser = setupBrowserWithReferrer(None)
-  >>> browser.open('http://launchpad.dev/people/+newteam')
+  >>> browser.open('http://launchpad.test/people/+newteam')
   >>> browser.getControl('Name', index=0).value = 'team3'
   >>> browser.getControl('Display Name').value = 'Team 3'
   >>> browser.getControl('Create').click()
@@ -58,7 +58,7 @@
 present a hopefully helpful error message.
 
   >>> browser.handleErrors = True
-  >>> browser.open('http://launchpad.dev/people/+newteam')
+  >>> browser.open('http://launchpad.test/people/+newteam')
   >>> browser.getControl('Name', index=0).value = 'team3'
   >>> browser.getControl('Display Name').value = 'Team 3'
   >>> browser.getControl('Create').click()
@@ -79,12 +79,12 @@
 
 To support apport, we allow it for +storeblob.
 
-  >>> browser.post('http://launchpad.dev/+storeblob', 'x=1')
+  >>> browser.post('http://launchpad.test/+storeblob', 'x=1')
 
 Similary, we exempt the URL /+hwdb/+submit in order to allow checkbox
 to submit HWDB reports.
 
-  >>> browser.post('http://launchpad.dev/+hwdb/+submit', 'x=1')
+  >>> browser.post('http://launchpad.test/+hwdb/+submit', 'x=1')
 
 To support old versions of launchpadlib, we also let POST requests
 without a REFERER header go through to +request-token and
@@ -92,13 +92,13 @@
 
   >>> body = ('oauth_signature=%26&oauth_consumer_key=test'
   ...         '&oauth_signature_method=PLAINTEXT')
-  >>> browser.post('http://launchpad.dev/+request-token', body)
+  >>> browser.post('http://launchpad.test/+request-token', body)
 
 This request results in a response code of 401, but if there was no
 exception for +access-token, it would result in an
 OffsiteFormPostError.
 
-  >>> browser.post('http://launchpad.dev/+access-token', 'x=1')
+  >>> browser.post('http://launchpad.test/+access-token', 'x=1')
   Traceback (most recent call last):
   ...
   HTTPError: HTTP Error 401: Unauthorized
@@ -112,12 +112,12 @@
   >>> allvhosts._hostnames.add('bzr.dev')
 
   >>> browser = setupBrowserWithReferrer('http://bzr.dev')
-  >>> browser.open('http://launchpad.dev/people/+newteam')
+  >>> browser.open('http://launchpad.test/people/+newteam')
   >>> browser.getControl('Name', index=0).value = 'team4'
   >>> browser.getControl('Display Name').value = 'Team 4'
   >>> browser.getControl('Create').click()
   >>> print browser.url
-  http://launchpad.dev/~team4
+  http://launchpad.test/~team4
 
   # Now restore our site's hostname.
   >>> allvhosts._hostnames.remove('bzr.dev')
@@ -133,13 +133,13 @@
   >>> browser = setupBrowserWithReferrer('http://evil.people.com/')
   >>> no_referrer_browser = setupBrowserWithReferrer(None)
 
-  >>> browser.post('http://launchpad.dev/api/devel/people', 'ws.op=foo&x=1')
+  >>> browser.post('http://launchpad.test/api/devel/people', 'ws.op=foo&x=1')
   Traceback (most recent call last):
   ...
   OffsiteFormPostError: http://evil.people.com/
 
   >>> no_referrer_browser.post(
-  ...     'http://launchpad.dev/api/devel/people', 'ws.op=foo&x=1')
+  ...     'http://launchpad.test/api/devel/people', 'ws.op=foo&x=1')
   Traceback (most recent call last):
   ...
   NoReferrerError: No value for REFERER header
@@ -148,13 +148,13 @@
 though it were signed with OAuth.
 
   >>> browser.post(
-  ...     'http://launchpad.dev/', 'oauth_consumer_key=foo&oauth_token=bar')
+  ...     'http://launchpad.test/', 'oauth_consumer_key=foo&oauth_token=bar')
   Traceback (most recent call last):
   ...
   OffsiteFormPostError: http://evil.people.com/
 
   >>> no_referrer_browser.post(
-  ...     'http://launchpad.dev/', 'oauth_consumer_key=foo&oauth_token=bar')
+  ...     'http://launchpad.test/', 'oauth_consumer_key=foo&oauth_token=bar')
   Traceback (most recent call last):
   ...
   NoReferrerError: No value for REFERER header
@@ -173,7 +173,7 @@
 But the browser-accessible API ignores OAuth credentials altogether.
 
   >>> browser.post(
-  ...     'http://launchpad.dev/api/devel/projects', sig)
+  ...     'http://launchpad.test/api/devel/projects', sig)
   Traceback (most recent call last):
   ...
   OffsiteFormPostError: http://evil.people.com/
@@ -181,7 +181,7 @@
 If you go through the 'api' vhost, the signed request will be
 processed despite the bogus referrer, but...
 
-  >>> browser.post('http://api.launchpad.dev/devel/projects', sig)
+  >>> browser.post('http://api.launchpad.test/devel/projects', sig)
   Traceback (most recent call last):
   ...
   NoneError: None isn't acceptable as a value for Product.owner

=== modified file 'lib/lp/app/stories/basics/xx-opstats.txt'
--- lib/lp/app/stories/basics/xx-opstats.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/app/stories/basics/xx-opstats.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
     >>> import xmlrpclib
     >>> from lp.testing.xmlrpc import XMLRPCTestTransport
     >>> lp_xmlrpc = xmlrpclib.ServerProxy(
-    ...     'http://xmlrpc.launchpad.dev/+opstats',
+    ...     'http://xmlrpc.launchpad.test/+opstats',
     ...     transport=XMLRPCTestTransport()
     ...     )
 
@@ -64,7 +64,7 @@
 Number of HTTP requests and success codes
 -----------------------------------------
 
-    >>> output = http("GET / HTTP/1.1\nHost: bugs.launchpad.dev\n")
+    >>> output = http("GET / HTTP/1.1\nHost: bugs.launchpad.test\n")
     >>> output.getStatus()
     200
     >>> report()
@@ -80,7 +80,7 @@
 information is not found in there, so a retry is attempted against the
 master DB in case the information is missing due to replication lag.
 
-    >>> output = http("GET http://launchpad.dev/non-existant HTTP/1.1\n")
+    >>> output = http("GET http://launchpad.test/non-existant HTTP/1.1\n")
     >>> output.getStatus()
     404
     >>> report()
@@ -105,7 +105,7 @@
     ...         raise Exception('Oops')
     ...
     >>> ztapi.browserView(None, "error-test", ErrorView)
-    >>> output = http("GET /error-test HTTP/1.1\nHost: launchpad.dev\n")
+    >>> output = http("GET /error-test HTTP/1.1\nHost: launchpad.test\n")
     >>> output.getStatus()
     500
     >>> report()
@@ -122,7 +122,7 @@
     >>> from textwrap import dedent
     >>> output = http(dedent("""\
     ...     GET /error-test HTTP/1.1
-    ...     Host: launchpad.dev
+    ...     Host: launchpad.test
     ...     User-Agent: Mozilla/42.0
     ...     """))
     >>> output.getStatus()
@@ -203,9 +203,9 @@
 
 Stats can also be retrieved via HTTP in cricket-graph format:
 
-    >>> output = http("GET / HTTP/1.1\nHost: launchpad.dev\n")
-    >>> output = http("GET / HTTP/1.1\nHost: launchpad.dev\n")
-    >>> print http("GET /+opstats HTTP/1.1\nHost: launchpad.dev\n")
+    >>> output = http("GET / HTTP/1.1\nHost: launchpad.test\n")
+    >>> output = http("GET / HTTP/1.1\nHost: launchpad.test\n")
+    >>> print http("GET /+opstats HTTP/1.1\nHost: launchpad.test\n")
     HTTP/1.1 200 Ok
     ...
     Content-Type: text/plain; charset=US-ASCII
@@ -258,7 +258,7 @@
 
 We can still access the opstats page.
 
-    >>> print http("GET /+opstats HTTP/1.1\nHost: launchpad.dev\n")
+    >>> print http("GET /+opstats HTTP/1.1\nHost: launchpad.test\n")
     HTTP/1.1 200 Ok
     ...
     Content-Type: text/plain; charset=US-ASCII
@@ -270,7 +270,7 @@
 
     >>> print http(r"""
     ... GET /+opstats HTTP/1.1
-    ... Host: launchpad.dev
+    ... Host: launchpad.test
     ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
     ... """)
     HTTP/1.1 200 Ok

=== modified file 'lib/lp/app/stories/basics/xx-pagetest-logging.txt'
--- lib/lp/app/stories/basics/xx-pagetest-logging.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/app/stories/basics/xx-pagetest-logging.txt	2019-05-22 15:20:07 +0000
@@ -3,9 +3,9 @@
 When running our pagetests we log all HTTP requests into the
 pagetests-access.log file.
 
-    >>> browser.open('http://launchpad.dev/')
+    >>> browser.open('http://launchpad.test/')
 
     >>> log = open('logs/pagetests-access.log').read()
     >>> print log.strip().split('\n')[-1]
-    127.0.0.88 - ... "launchpad.dev" [...] "GET / HTTP/1.1" 200 ...
+    127.0.0.88 - ... "launchpad.test" [...] "GET / HTTP/1.1" 200 ...
     "Anonymous" "RootObject:index.html" "" "Python-urllib/..."

=== modified file 'lib/lp/app/stories/basics/xx-preferred-charsets.txt'
--- lib/lp/app/stories/basics/xx-preferred-charsets.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/app/stories/basics/xx-preferred-charsets.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 that it doesn't accept it.
 
     >>> anon_browser.addHeader('Accept-Charset', 'iso8859-1')
-    >>> anon_browser.open('http://launchpad.dev/')
+    >>> anon_browser.open('http://launchpad.test/')
     >>> anon_browser.headers['Content-Type']
     'text/html;charset=utf-8'
 

=== modified file 'lib/lp/app/stories/form/xx-form-layout.txt'
--- lib/lp/app/stories/form/xx-form-layout.txt	2012-08-15 22:01:39 +0000
+++ lib/lp/app/stories/form/xx-form-layout.txt	2019-05-22 15:20:07 +0000
@@ -11,7 +11,7 @@
 
 The new team form contains an example of a normal text widget.
 
-    >>> user_browser.open('http://launchpad.dev/people/+newteam')
+    >>> user_browser.open('http://launchpad.test/people/+newteam')
     >>> content = find_main_content(user_browser.contents)
     >>> print content
     <...
@@ -53,7 +53,7 @@
 Checkboxes have their label to the right. Let's look at one example.
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/firefox/+review-license')
+    ...     'http://launchpad.test/firefox/+review-license')
     >>> print find_tag_by_id(admin_browser.contents, 'launchpad-form-widgets')
     <...
     <tr>
@@ -74,7 +74,7 @@
 just the form content to be rendered for any URL corresponding to an
 LPFormView:
 
-    >>> admin_browser.open('http://launchpad.dev/evolution/+edit/++form++')
+    >>> admin_browser.open('http://launchpad.test/evolution/+edit/++form++')
     >>> print admin_browser.contents
     <div...
     <table class="form" id="launchpad-form-widgets">
@@ -86,7 +86,7 @@
     >>> cprov_browser = setupBrowser(
     ...     auth='Basic celso.providelo@xxxxxxxxxxxxx:test')
     >>> cprov_browser.open(
-    ...   'http://launchpad.dev/~cprov/+archive/ppa/+edit/++form++')
+    ...   'http://launchpad.test/~cprov/+archive/ppa/+edit/++form++')
     >>> print cprov_browser.contents
     <div...
     <table class="form" id="launchpad-form-widgets">
@@ -97,7 +97,7 @@
 display forms.
 
     >>> cprov_browser.open(
-    ...     'http://launchpad.dev/~cprov/+editsshkeys/++form++')
+    ...     'http://launchpad.test/~cprov/+editsshkeys/++form++')
     Traceback (most recent call last):
     ...
     NotFound...
@@ -105,7 +105,7 @@
 Nor will it allow unauthorized access to data that it should not present.
 
     >>> browser.open(
-    ...     'http://launchpad.dev/~cprov/+archive/ppa/+edit/++form++')
+    ...     'http://launchpad.test/~cprov/+archive/ppa/+edit/++form++')
     Traceback (most recent call last):
     ...
     Unauthorized...

=== modified file 'lib/lp/app/stories/launchpad-root/front-pages.txt'
--- lib/lp/app/stories/launchpad-root/front-pages.txt	2012-08-14 14:30:59 +0000
+++ lib/lp/app/stories/launchpad-root/front-pages.txt	2019-05-22 15:20:07 +0000
@@ -5,9 +5,9 @@
 
     >>> from lp.services.features.testing import FeatureFixture
     >>> with FeatureFixture({"app.root_blog.enabled": True}):
-    ...     browser.open('http://launchpad.dev/')
+    ...     browser.open('http://launchpad.test/')
     >>> browser.url
-    'http://launchpad.dev/'
+    'http://launchpad.test/'
 
 It contains a string which our IS uses to determine whether or not
 Launchpad is alive:
@@ -22,7 +22,7 @@
 
     >>> code_link = browser.getLink(url='code')
     >>> code_link.url
-    'http://code.launchpad.dev/'
+    'http://code.launchpad.test/'
 
 It also includes a search form...
 
@@ -60,40 +60,40 @@
 The homepage looks different when the user is logged in:
 
     >>> user_browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
-    >>> user_browser.open('http://launchpad.dev/')
+    >>> user_browser.open('http://launchpad.test/')
 
 Now there are links to create projects and teams:
 
     >>> project_link = user_browser.getLink(url='/projects')
     >>> project_link.url
-    'http://launchpad.dev/projects/+new'
+    'http://launchpad.test/projects/+new'
 
     >>> people_link = user_browser.getLink(url='/people')
     >>> people_link.url
-    'http://launchpad.dev/people/+newteam'
+    'http://launchpad.test/people/+newteam'
 
 
 The front pages for the other applications, however, do have
 application tabs. On these pages, the tab normally named "Overview"
 is labelled "Launchpad Home" to make clear where it goes.
 
-    >>> anon_browser.open('http://code.launchpad.dev/')
+    >>> anon_browser.open('http://code.launchpad.test/')
     >>> print_location_apps(anon_browser.contents)
-    * Launchpad Home - http://launchpad.dev/
-    * Code (selected) - http://code.launchpad.dev/
-    * Bugs - http://bugs.launchpad.dev/
-    * Blueprints - http://blueprints.launchpad.dev/
-    * Translations - http://translations.launchpad.dev/
-    * Answers - http://answers.launchpad.dev/
+    * Launchpad Home - http://launchpad.test/
+    * Code (selected) - http://code.launchpad.test/
+    * Bugs - http://bugs.launchpad.test/
+    * Blueprints - http://blueprints.launchpad.test/
+    * Translations - http://translations.launchpad.test/
+    * Answers - http://answers.launchpad.test/
 
-    >>> user_browser.open('http://answers.launchpad.dev/')
+    >>> user_browser.open('http://answers.launchpad.test/')
     >>> print_location_apps(user_browser.contents)
-    * Launchpad Home - http://launchpad.dev/
-    * Code - http://code.launchpad.dev/
-    * Bugs - http://bugs.launchpad.dev/
-    * Blueprints - http://blueprints.launchpad.dev/
-    * Translations - http://translations.launchpad.dev/
-    * Answers (selected) - http://answers.launchpad.dev/
+    * Launchpad Home - http://launchpad.test/
+    * Code - http://code.launchpad.test/
+    * Bugs - http://bugs.launchpad.test/
+    * Blueprints - http://blueprints.launchpad.test/
+    * Translations - http://translations.launchpad.test/
+    * Answers (selected) - http://answers.launchpad.test/
 
 The footer of those pages contains the link to the front page, tour
 and guide:

=== modified file 'lib/lp/app/stories/launchpad-root/xx-featuredprojects.txt'
--- lib/lp/app/stories/launchpad-root/xx-featuredprojects.txt	2012-06-11 00:47:38 +0000
+++ lib/lp/app/stories/launchpad-root/xx-featuredprojects.txt	2019-05-22 15:20:07 +0000
@@ -23,7 +23,7 @@
 Anonymous users will see the list of featured projects with links to the
 projects' pages in Launchpad. The "project of the day" is listed separately.
 
-    >>> anon_browser.open('http://launchpad.dev/')
+    >>> anon_browser.open('http://launchpad.test/')
     >>> featured = find_tag_by_id(anon_browser.contents, 'homepage-featured')
     >>> print extract_text(featured.h2)
     Featured projects
@@ -55,7 +55,7 @@
 
 A user without privileges cannot see the administration link, either:
 
-    >>> user_browser.open('http://launchpad.dev/')
+    >>> user_browser.open('http://launchpad.test/')
     >>> user_browser.getLink(MANAGE_LINK)
     Traceback (most recent call last):
     ...
@@ -63,14 +63,14 @@
 
 But Foo Bar, who is an administrator, can see the management link:
 
-    >>> admin_browser.open('http://launchpad.dev/')
+    >>> admin_browser.open('http://launchpad.test/')
     >>> admin_browser.getLink(MANAGE_LINK).click()
     >>> admin_browser.url
-    'http://launchpad.dev/+featuredprojects'
+    'http://launchpad.test/+featuredprojects'
 
 No Privilege persons is denied access to this page:
 
-    >>> user_browser.open('http://launchpad.dev/+featuredprojects')
+    >>> user_browser.open('http://launchpad.test/+featuredprojects')
     Traceback (most recent call last):
     ...
     Unauthorized: ...
@@ -81,7 +81,7 @@
     >>> admin_browser.getControl('Add project').value = 'apache'
     >>> admin_browser.getControl('Update').click()
     >>> admin_browser.url
-    'http://launchpad.dev/'
+    'http://launchpad.test/'
     >>> featured = find_tag_by_id(admin_browser.contents, 'homepage-featured')
     >>> 'Apache' in extract_text(featured)
     True
@@ -90,7 +90,7 @@
 that Apache has been added. Because the list has changed, a different project
 is now at index '4' and is therefore displayed as the top project:
 
-    >>> anon_browser.open('http://launchpad.dev/')
+    >>> anon_browser.open('http://launchpad.test/')
     >>> featured = find_tag_by_id(anon_browser.contents, 'homepage-featured')
     >>> top_project = featured.find('', 'featured-project-top')
     >>> print extract_text(top_project.h3)
@@ -115,7 +115,7 @@
     >>> admin_browser.getControl('Apache').click()
     >>> admin_browser.getControl('Update').click()
     >>> admin_browser.url
-    'http://launchpad.dev/'
+    'http://launchpad.test/'
     >>> featured = find_tag_by_id(admin_browser.contents, 'homepage-featured')
     >>> 'Apache' in extract_text(featured)
     False
@@ -123,7 +123,7 @@
 Just to be certain, we will iterate the list as we did before and see
 that Apache has been removed:
 
-    >>> anon_browser.open('http://launchpad.dev/')
+    >>> anon_browser.open('http://launchpad.test/')
     >>> featured = find_tag_by_id(anon_browser.contents, 'homepage-featured')
     >>> for link in featured.findAll('a'):
     ...     print extract_text(link)

=== modified file 'lib/lp/app/stories/launchpad-search/site-search.txt'
--- lib/lp/app/stories/launchpad-search/site-search.txt	2018-04-13 20:47:03 +0000
+++ lib/lp/app/stories/launchpad-search/site-search.txt	2019-05-22 15:20:07 +0000
@@ -27,10 +27,10 @@
 
 The search form is available on almost every Launchpad page.
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu')
+    >>> anon_browser.open('http://launchpad.test/ubuntu')
     >>> search_for('test1')
     >>> print anon_browser.url
-    http://launchpad.dev/+search?field.text=test1
+    http://launchpad.test/+search?field.text=test1
 
 But the search results page has its own search form, so the global one
 is omitted.
@@ -43,7 +43,7 @@
 If by chance someone ends up at /+search with no search parameters, they
 get an explanation of the search function.
 
-    >>> anon_browser.open('http://launchpad.dev/+search')
+    >>> anon_browser.open('http://launchpad.test/+search')
     >>> print anon_browser.title
     Search Launchpad
 
@@ -258,7 +258,7 @@
     <p id="no-page-service">
     The page search service was not available when this search was
     performed.
-    <a href="http://launchpad.dev/+search?field.text=gnomebaker";>Search
+    <a href="http://launchpad.test/+search?field.text=gnomebaker";>Search
     again</a> to see the matching pages.
     </p>
 
@@ -302,19 +302,19 @@
 Most pages have the global search form in them. Any user can enter terms
 in the page they are viewing and submit the form to see the results.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox')
     >>> print anon_browser.title
     Bugs : Mozilla Firefox
 
     >>> print anon_browser.url
-    http://bugs.launchpad.dev/firefox
+    http://bugs.launchpad.test/firefox
 
     >>> search_for('mozilla')
     >>> print anon_browser.title
     Pages matching "mozilla" in Launchpad
 
     >>> print anon_browser.url
-    http://launchpad.dev/+search?...
+    http://launchpad.test/+search?...
 
     >>> print_search_results()
     Exact matches
@@ -341,7 +341,7 @@
     ...     visibility=PersonVisibility.PRIVATE)
     >>> logout()
     >>> browser = setupBrowser(auth='Basic salgado@xxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.dev/+search')
+    >>> browser.open('http://launchpad.test/+search')
     >>> search_for('Private Benjamin', browser=browser)
     >>> print_search_results(browser.contents)
     Exact matches
@@ -352,7 +352,7 @@
 A user who is not in the private team will not see the team listed in
 the results.
 
-    >>> user_browser.open('http://launchpad.dev/+search')
+    >>> user_browser.open('http://launchpad.test/+search')
     >>> search_for('Private Benjamin', browser=user_browser)
     >>> print_search_results(user_browser.contents)
 

=== modified file 'lib/lp/app/tests/test_services.py'
--- lib/lp/app/tests/test_services.py	2017-05-08 11:38:20 +0000
+++ lib/lp/app/tests/test_services.py	2019-05-22 15:20:07 +0000
@@ -44,7 +44,7 @@
         fake_service = FakeService()
         self.registerUtility(fake_service, IService, "fake")
         context, view, request = test_traverse(
-            'https://launchpad.dev/api/devel/+services/fake')
+            'https://launchpad.test/api/devel/+services/fake')
         self.assertEqual(getUtility(IServiceFactory), context)
         self.assertEqual(fake_service, view)
 
@@ -53,11 +53,11 @@
         self.useFixture(FakeLogger())
         self.assertRaises(
             NotFound, self.getUserBrowser,
-            'https://launchpad.dev/api/devel/+services')
+            'https://launchpad.test/api/devel/+services')
 
     def test_invalid_service(self):
         # Test that traversal an invalid service name fails.
         self.useFixture(FakeLogger())
         self.assertRaises(
             NotFound, self.getUserBrowser,
-            'https://launchpad.dev/api/devel/+services/invalid')
+            'https://launchpad.test/api/devel/+services/invalid')

=== modified file 'lib/lp/app/tests/test_tales.py'
--- lib/lp/app/tests/test_tales.py	2018-01-02 16:10:26 +0000
+++ lib/lp/app/tests/test_tales.py	2019-05-22 15:20:07 +0000
@@ -61,7 +61,7 @@
     >>> class FakeApplicationRequest:
     ...    principal = FakePrincipal()
     ...    def getURL(self):
-    ...        return 'http://launchpad.dev/'
+    ...        return 'http://launchpad.test/'
     ...
 
     Let's make a fake request, where request.principal is a FakePrincipal

=== modified file 'lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py'
--- lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2018-02-02 11:40:34 +0000
+++ lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2019-05-22 15:20:07 +0000
@@ -547,7 +547,7 @@
             "of the archive to verify it.\n\n"
             "You can contact the archive owner by visiting their Launchpad "
                 "page here:\n\n"
-            "<http://launchpad.dev/~joe>\n\n"
+            "<http://launchpad.test/~joe>\n\n"
             "If you have any concerns you can contact the Launchpad team by "
                 "emailing\n"
             "feedback@xxxxxxxxxxxxx\n\n"

=== modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt'
--- lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2015-09-02 12:05:15 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2019-05-22 15:20:07 +0000
@@ -518,7 +518,7 @@
     DEBUG Date: Tue, 25 Apr 2006 10:36:14 -0300
     DEBUG Changed-By: cprov@xxxxxxxxxx (Celso R. Providelo)
     DEBUG Maintainer: Launchpad team <launchpad@xxxxxxxxxxxxxxxxxxx>
-    DEBUG http://launchpad.dev/ubuntu/+source/bar/1.0-4
+    DEBUG http://launchpad.test/ubuntu/+source/bar/1.0-4
     DEBUG
     DEBUG ==
     DEBUG
@@ -698,7 +698,7 @@
     Maintainer: Non-ascii maintainer =C4=8Ciha=C5=99 <launchpad@lists.canonical=
     .com>
     Signed-By: Foo Bar <foo.bar@xxxxxxxxxxxxx>
-    http://launchpad.dev/ubuntu/+source/bar/1.0-10
+    http://launchpad.test/ubuntu/+source/bar/1.0-10
     <BLANKLINE>
     =3D=3D
     <BLANKLINE>

=== modified file 'lib/lp/blueprints/browser/tests/sprintattendance-views.txt'
--- lib/lp/blueprints/browser/tests/sprintattendance-views.txt	2012-04-10 14:01:17 +0000
+++ lib/lp/blueprints/browser/tests/sprintattendance-views.txt	2019-05-22 15:20:07 +0000
@@ -22,10 +22,10 @@
 The view also defines a next_url and cancel_url.
 
     >>> print sprint_attendance_view.next_url
-    http://launchpad.dev/sprints/ubz
+    http://launchpad.test/sprints/ubz
 
     >>> print sprint_attendance_view.cancel_url
-    http://launchpad.dev/sprints/ubz
+    http://launchpad.test/sprints/ubz
 
 A helper function to test date handling.
 

=== modified file 'lib/lp/blueprints/browser/tests/test_specification.py'
--- lib/lp/blueprints/browser/tests/test_specification.py	2017-11-10 11:28:43 +0000
+++ lib/lp/blueprints/browser/tests/test_specification.py	2019-05-22 15:20:07 +0000
@@ -368,8 +368,8 @@
             browser.contents, soupmatchers.HTMLContains(privacy_banner))
 
 
-# canonical_url erroneously returns http://blueprints.launchpad.dev/+new
-NEW_SPEC_FROM_ROOT_URL = 'http://blueprints.launchpad.dev/specs/+new'
+# canonical_url erroneously returns http://blueprints.launchpad.test/+new
+NEW_SPEC_FROM_ROOT_URL = 'http://blueprints.launchpad.test/specs/+new'
 
 
 class NewSpecificationTests:

=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
--- lib/lp/blueprints/browser/tests/test_specificationtarget.py	2017-11-10 11:28:43 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py	2019-05-22 15:20:07 +0000
@@ -43,7 +43,7 @@
         view = create_view(
             context, '+register-a-blueprint-button')
         self.assertEqual(
-            'http://blueprints.launchpad.dev/%s/+addspec' % name,
+            'http://blueprints.launchpad.test/%s/+addspec' % name,
             view.target_url)
         self.assertTrue(
             '<div id="involvement" class="portlet involvement">' in view())

=== modified file 'lib/lp/blueprints/doc/specification-notifications.txt'
--- lib/lp/blueprints/doc/specification-notifications.txt	2018-06-02 00:27:50 +0000
+++ lib/lp/blueprints/doc/specification-notifications.txt	2019-05-22 15:20:07 +0000
@@ -135,7 +135,7 @@
     <BLANKLINE>
     --
     Support Native SVG Objects
-    http://blueprints.launchpad.dev/firefox/+spec/svg-support
+    http://blueprints.launchpad.test/firefox/+spec/svg-support
     <BLANKLINE>
 
 Whiteboard change:
@@ -179,7 +179,7 @@
     <BLANKLINE>
     --
     Support Native SVG Objects
-    http://blueprints.launchpad.dev/firefox/+spec/svg-support
+    http://blueprints.launchpad.test/firefox/+spec/svg-support
     <BLANKLINE>
 
 
@@ -222,7 +222,7 @@
     <BLANKLINE>
     --
     Support Native SVG Objects
-    http://blueprints.launchpad.dev/firefox/+spec/svg-support
+    http://blueprints.launchpad.test/firefox/+spec/svg-support
     <BLANKLINE>
 
 Change priority:
@@ -255,7 +255,7 @@
     <BLANKLINE>
     --
     Support Native SVG Objects
-    http://blueprints.launchpad.dev/firefox/+spec/svg-support
+    http://blueprints.launchpad.test/firefox/+spec/svg-support
     <BLANKLINE>
 
 Change approver, assignee and drafter:
@@ -291,7 +291,7 @@
     <BLANKLINE>
     --
     Support Native SVG Objects
-    http://blueprints.launchpad.dev/firefox/+spec/svg-support
+    http://blueprints.launchpad.test/firefox/+spec/svg-support
     <BLANKLINE>
 
 If we do a change, which we don't yet support sending a notification

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-buglinks.txt'
--- lib/lp/blueprints/stories/blueprints/xx-buglinks.txt	2017-10-23 00:16:39 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-buglinks.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 the 'Related Bugs' section.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://launchpad.test/firefox/+spec/svg-support')
     >>> print extract_text(find_tag_by_id(anon_browser.contents, 'bug_links'))
     Related bugs
     Bug #1: Firefox does not support SVG   New
@@ -24,12 +24,12 @@
 This link is only available to registered users:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/svg-support/+linkbug')
+    ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support/+linkbug')
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support/+linkbug'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support/+linkbug'
     >>> back_link = user_browser.getLink('Support Native SVG Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
 
 To link the bug, the user enters the bug ID and clicks the 'Add'
 button.
@@ -53,7 +53,7 @@
 
     >>> user_browser.getLink('Unlink a bug').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support/+unlinkbug'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support/+unlinkbug'
 
 The list of linked bugs is displayed. The user selects the bug to remove
 and clicks the 'Remove' button.

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-creation.txt'
--- lib/lp/blueprints/stories/blueprints/xx-creation.txt	2017-12-24 15:45:26 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-creation.txt	2019-05-22 15:20:07 +0000
@@ -61,7 +61,7 @@
 
 Starting from the Blueprints home page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/')
+    >>> user_browser.open('http://blueprints.launchpad.test/')
 
 Users can press the graphical "Register a blueprint" button:
 
@@ -74,7 +74,7 @@
 
 Starting from the Ubuntu distribution page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu')
 
 Users can use the ISpecificationTarget involvement menu to register a
 blueprint.
@@ -89,7 +89,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-       href="http://blueprints.launchpad.dev/ubuntu/+addspec";>Register
+       href="http://blueprints.launchpad.test/ubuntu/+addspec";>Register
        a blueprint</a>
 
 
@@ -98,7 +98,7 @@
 
 Starting from the Ubuntu Hoary distribution series page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/hoary')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/hoary')
 
 Users can also follow the textual "Register a blueprint" link:
 
@@ -106,7 +106,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-       href="http://blueprints.launchpad.dev/ubuntu/hoary/+addspec";>Register
+       href="http://blueprints.launchpad.test/ubuntu/hoary/+addspec";>Register
        a blueprint</a>
 
 
@@ -115,7 +115,7 @@
 
 Starting from the Bazaar product page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/bzr')
+    >>> user_browser.open('http://blueprints.launchpad.test/bzr')
 
 
 Users can also follow the textual "Register a blueprint" link:
@@ -124,7 +124,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-       href="http://blueprints.launchpad.dev/bzr/+addspec";>Register
+       href="http://blueprints.launchpad.test/bzr/+addspec";>Register
        a blueprint</a>
 
 For products without any blueprints, users can follow the special "register
@@ -132,7 +132,7 @@
 
     >>> user_browser.getLink('register it here as a blueprint').click()
     >>> print user_browser.url
-    http://blueprints.launchpad.dev/bzr/+addspec
+    http://blueprints.launchpad.test/bzr/+addspec
     >>> print user_browser.title
     Register a blueprint in...
     >>> print extract_text(find_main_content(user_browser.contents))
@@ -143,7 +143,7 @@
 
 Starting from the Mozilla Firefox product series page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/firefox/1.0')
+    >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
 
 
 Users can also follow the textual "Register a blueprint" link:
@@ -152,7 +152,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-       href="http://blueprints.launchpad.dev/firefox/1.0/+addspec";>Register
+       href="http://blueprints.launchpad.test/firefox/1.0/+addspec";>Register
        a blueprint</a>
 
 
@@ -161,7 +161,7 @@
 
 Starting from the Mozilla project page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/mozilla')
+    >>> user_browser.open('http://blueprints.launchpad.test/mozilla')
 
 Users can follow the textual "Register a blueprint" link:
 
@@ -169,7 +169,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-       href="http://blueprints.launchpad.dev/mozilla/+addspec";>Register
+       href="http://blueprints.launchpad.test/mozilla/+addspec";>Register
        a blueprint</a>
 
 
@@ -178,7 +178,7 @@
 
 Starting from the Future Mega Meeting sprint page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/sprints/futurista')
+    >>> user_browser.open('http://blueprints.launchpad.test/sprints/futurista')
 
 Users can also follow the textual "Register a blueprint" link:
 
@@ -186,7 +186,7 @@
     ...     user_browser.contents, 'menu-link-new'):
     ...     print tag
     <a class="menu-link-new..."
-     href="http://blueprints.launchpad.dev/sprints/futurista/+addspec";>Register
+     href="http://blueprints.launchpad.test/sprints/futurista/+addspec";>Register
        a blueprint</a>
 
 
@@ -203,12 +203,12 @@
 
 We'll start from the default blueprint registration form:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/specs/+new')
+    >>> user_browser.open('http://blueprints.launchpad.test/specs/+new')
 
 Canceling creation, brings one back to the blueprints home page.
 
     >>> user_browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/'
+    'http://blueprints.launchpad.test/'
 
 When a blueprint is registered from the Blueprints home page, Launchpad
 requires the user to specify a target for the new blueprint. This target
@@ -248,7 +248,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/networkmagic'
+    'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic'
     >>> print user_browser.title
     Network Magic: Auto Network Detection...
 
@@ -261,7 +261,7 @@
 
 Let's register a blueprint from the Ubuntu distribution:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> control('Name').value = 'networkmagic-1'
     >>> control('Title').value = 'Network Magic: Auto Network Detection'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic-1'
@@ -272,7 +272,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/networkmagic-1'
+    'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-1'
     >>> print user_browser.title
     Network Magic: Auto Network Detection...
 
@@ -288,7 +288,7 @@
 Let's register a blueprint from the Ubuntu Hoary distribution series:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/ubuntu/hoary/+addspec')
+    ...     'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
     >>> control('Name').value = 'networkmagic-2'
     >>> control('Title').value = 'Network Magic: Auto Network Detection'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic2'
@@ -297,7 +297,7 @@
 Canceling creation, brings one back to the blueprints Hoary home.
 
     >>> user_browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/ubuntu/hoary'
+    'http://blueprints.launchpad.test/ubuntu/hoary'
 
 By default, blueprints are not proposed as series goals:
 
@@ -309,7 +309,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/networkmagic-2'
+    'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-2'
     >>> print user_browser.title
     Network Magic: Auto Network Detection...
 
@@ -321,7 +321,7 @@
 Let's register another blueprint from the Mozilla Firefox 1.0 product series:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/ubuntu/hoary/+addspec')
+    ...     'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
     >>> control('Name').value = 'networkmagic-3'
     >>> control('Title').value = 'Network Magic: Auto Network Detection'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic3'
@@ -336,7 +336,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/networkmagic-3'
+    'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-3'
     >>> print user_browser.title
     Network Magic: Auto Network Detection...
 
@@ -350,7 +350,7 @@
 for the series, the new blueprint is automatically accepted as a series goal:
 
     >>> admin_browser.open(
-    ...     'http://blueprints.launchpad.dev/ubuntu/hoary/+addspec')
+    ...     'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
     >>> control = admin_browser.getControl
     >>> control('Name').value = 'networkmagic-4'
     >>> control('Title').value = 'Network Magic: Auto Network Detection'
@@ -373,7 +373,7 @@
 
 Let's register a blueprint from the Mozilla Firefox product:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/firefox/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/firefox/+addspec')
     >>> control = user_browser.getControl
     >>> control('Name').value = 'svg-support-1'
     >>> control('Title').value = 'SVG Support'
@@ -383,14 +383,14 @@
 Canceling creation, brings one back to the blueprints Firefox home.
 
     >>> user_browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/firefox'
+    'http://blueprints.launchpad.test/firefox'
 
 Pressing the "Register Blueprint" button creates a blueprint targeted to the
 Mozilla Firefox product, then redirects the user to the new blueprint's page:
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support-1'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support-1'
     >>> print user_browser.title
     SVG Support...
 
@@ -406,7 +406,7 @@
 Let's register a blueprint from the Mozilla Firefox 1.0 product series:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/1.0/+addspec')
+    ...     'http://blueprints.launchpad.test/firefox/1.0/+addspec')
     >>> control('Name').value = 'svg-support-2'
     >>> control('Title').value = 'SVG Support'
     >>> control('URL').value = 'http://wiki.firefox.com/SvgSupport2'
@@ -422,7 +422,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support-2'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support-2'
     >>> print user_browser.title
     SVG Support...
 
@@ -434,7 +434,7 @@
 Let's register another blueprint from the Mozilla Firefox 1.0 product series:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/1.0/+addspec')
+    ...     'http://blueprints.launchpad.test/firefox/1.0/+addspec')
     >>> control('Name').value = 'svg-support-3'
     >>> control('Title').value = 'SVG Support'
     >>> control('URL').value = 'http://wiki.firefox.com/SvgSupport3'
@@ -449,7 +449,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support-3'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support-3'
     >>> print user_browser.title
     SVG Support...
 
@@ -463,7 +463,7 @@
 for the series, the new blueprint is automatically accepted as a series goal:
 
     >>> admin_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/1.0/+addspec')
+    ...     'http://blueprints.launchpad.test/firefox/1.0/+addspec')
     >>> control = admin_browser.getControl
     >>> control('Name').value = 'svg-support-4'
     >>> control('Title').value = 'SVG Support'
@@ -483,7 +483,7 @@
 
 Let's register a blueprint from the Mozilla project:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/mozilla/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/mozilla/+addspec')
 
 When a blueprint is registered from a project, Launchpad requires the user to
 provide a target for the new blueprint. This target must be an existing
@@ -507,7 +507,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support-5'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support-5'
     >>> print user_browser.title
     SVG Support...
 
@@ -521,7 +521,7 @@
 Let's register a blueprint from the Future Mega Meeting sprint:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/sprints/futurista/+addspec')
+    ...     'http://blueprints.launchpad.test/sprints/futurista/+addspec')
 
 Since sprints by themselves are not directly related to distributions or
 products, Launchpad requires the user to specify a target for the new
@@ -544,7 +544,7 @@
 
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/sprints/futurista'
+    'http://blueprints.launchpad.test/sprints/futurista'
     >>> print extract_text(find_main_content(user_browser.contents))
     Blueprints for Future Mega Meeting...
 
@@ -552,7 +552,7 @@
 discussion at the sprint:
 
     >>> admin_browser.open(
-    ...     'http://blueprints.launchpad.dev/sprints/futurista/+settopics')
+    ...     'http://blueprints.launchpad.test/sprints/futurista/+settopics')
     >>> print(find_tag_by_id(admin_browser.contents, 'speclisting'))
     <...darcs-imports...
 
@@ -561,7 +561,7 @@
 a sprint topic:
 
     >>> admin_browser.open(
-    ...     'http://blueprints.launchpad.dev/sprints/futurista/+addspec')
+    ...     'http://blueprints.launchpad.test/sprints/futurista/+addspec')
     >>> control = admin_browser.getControl
     >>> control('For').value = 'bzr'
     >>> control('Name').value = 'darcs-imports-2'
@@ -599,7 +599,7 @@
     >>> logout()
 
     >>> sample_browser = setupBrowser('Basic test@xxxxxxxxxxxxx:test')
-    >>> sample_browser.open('http://blueprints.launchpad.dev/jokosher')
+    >>> sample_browser.open('http://blueprints.launchpad.test/jokosher')
     >>> sample_browser.getLink('Register a blueprint').click()
     >>> sample_browser.getControl('Name').value = 'spec-for-sprint'
     >>> sample_browser.getControl('Title').value = 'Spec for Sprint'
@@ -608,10 +608,10 @@
     >>> sample_browser.getControl('Propose for sprint').value = ['rome']
     >>> sample_browser.getControl('Register Blueprint').click()
     >>> sample_browser.url
-    'http://blueprints.launchpad.dev/jokosher/+spec/spec-for-sprint'
+    'http://blueprints.launchpad.test/jokosher/+spec/spec-for-sprint'
     >>> print sample_browser.title
     Spec for Sprint...
-    >>> sample_browser.open('http://blueprints.launchpad.dev/sprints/rome')
+    >>> sample_browser.open('http://blueprints.launchpad.test/sprints/rome')
     >>> find_tag_by_id(sample_browser.contents, 'speclisting')
     <...spec-for-sprint...>
 
@@ -628,7 +628,7 @@
 Attempting to register a duplicate blueprint from a target context produces
 an error:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> control = user_browser.getControl
     >>> control('Name').value = 'networkmagic'
     >>> control('Title').value = 'Network Magic: Automatic Network Detection'
@@ -636,7 +636,7 @@
     >>> control('Summary').value = summary
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+addspec'
+    'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...      print message.renderContents()
     There is 1 error...already in use by another blueprint...
@@ -644,7 +644,7 @@
 Attempting to register a duplicate blueprint from a non-target context
 produces the same error:
 
-    >>> url = 'http://blueprints.launchpad.dev/sprints/rome/+addspec'
+    >>> url = 'http://blueprints.launchpad.test/sprints/rome/+addspec'
     >>> user_browser.open(url)
     >>> user_browser.getControl('For').value = 'ubuntu'
     >>> user_browser.getControl('Name').value = 'media-integrity-check'
@@ -654,7 +654,7 @@
     ...     'There is already a blueprint with this name')
     >>> user_browser.getControl('Register Blueprint').click()
     >>> print user_browser.url
-    http://blueprints.launchpad.dev/sprints/rome/+addspec
+    http://blueprints.launchpad.test/sprints/rome/+addspec
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...      print message.renderContents()
     There is 1 error...already in use by another blueprint...
@@ -665,14 +665,14 @@
 
 Blueprint names must conform to a set pattern:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> control('Name').value = 'NetworkMagic!'
     >>> control('Title').value = 'Network Magic: Automatic Network Detection'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagicBang'
     >>> control('Summary').value = summary
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+addspec'
+    'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...      print message.renderContents()
     There is 1 error...Invalid name...
@@ -682,14 +682,14 @@
 converting upper case characters to their lower case equivalents, this is done
 automatically for the user:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> control('Name').value = 'New-Network-Magic'
     >>> control('Title').value = 'Network Magic: Automatic Network Detection'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NewNetworkMagic'
     >>> control('Summary').value = summary
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/new-network-magic'
+    'http://blueprints.launchpad.test/ubuntu/+spec/new-network-magic'
     >>> print user_browser.title
     Network Magic: Automatic Network Detection...
 
@@ -700,14 +700,14 @@
 It's not possible to register a blueprint with the same URL as an existing
 blueprint:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> control('Name').value = 'dupenetworkmagic'
     >>> control('Title').value = 'This is a dupe Network Magic Spec'
     >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic'
     >>> control('Summary').value = summary
     >>> control('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+addspec'
+    'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...      print message.renderContents()
     There is 1 error...is already registered by...
@@ -721,7 +721,7 @@
 a blueprint. To start with, it's not possible to register a blueprint from an
 individual user's blueprint listing page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/~mark')
+    >>> user_browser.open('http://blueprints.launchpad.test/~mark')
     >>> print user_browser.getLink('Register a blueprint')
     Traceback (most recent call last):
         ...
@@ -730,7 +730,7 @@
 It's also not possible to register a blueprint from a group's blueprint
 listing page:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/~admins')
+    >>> user_browser.open('http://blueprints.launchpad.test/~admins')
     >>> print user_browser.getLink('Register a blueprint')
     Traceback (most recent call last):
         ...

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-dependencies.txt'
--- lib/lp/blueprints/stories/blueprints/xx-dependencies.txt	2013-05-24 07:25:24 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-dependencies.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 
   >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
   >>> owner_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> print find_main_content(owner_browser.contents)
   <...
   ...Support E4X in EcmaScript...
@@ -26,24 +26,24 @@
 
   >>> owner_browser.getLink('Add dependency').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas/+linkdependency'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas/+linkdependency'
 
 One can decide not to add a dependency after all.
 
   >>> owner_browser.getLink('Cancel').url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 This +linkdependency page and the link to it are only accessible by
 users with launchpad.Edit permission for the blueprint.
 
   >>> user_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> user_browser.getLink('Add dependency')
   Traceback (most recent call last):
   ...
   LinkNotFoundError
   >>> user_browser.open(
-  ...  'http://blueprints.launchpad.dev/firefox/+spec/canvas/+linkdependency')
+  ...  'http://blueprints.launchpad.test/firefox/+spec/canvas/+linkdependency')
   Traceback (most recent call last):
   ...
   Unauthorized: ...
@@ -53,7 +53,7 @@
 
     >>> back_link = owner_browser.getLink('Support <canvas> Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+    'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 Now, lets POST the form, saying we want extension-manager-upgrades as the
 dependency.
@@ -62,7 +62,7 @@
   ...     'Depends On').value = 'extension-manager-upgrades'
   >>> owner_browser.getControl('Continue').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 
 == Removing a dependency ==
@@ -73,12 +73,12 @@
 
   >>> owner_browser.getLink('Remove dependency').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas/+removedependency'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas/+removedependency'
 
 One can decide not to remove a dependency after all.
 
   >>> owner_browser.getLink('Cancel').url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 Now, we make sure we can load the page. It should show two potential
 dependencies we could remove. The extension manager one, and "e4x".
@@ -91,7 +91,7 @@
 
     >>> back_link = owner_browser.getLink('Support <canvas> Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+    'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 We'll POST the form selecting "extension-manager-upgrades" for removal. We
 expect to be redirected to the blueprint page.
@@ -100,19 +100,19 @@
   ...     'Dependency').value = ['1']
   >>> owner_browser.getControl('Continue').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 This +removedependency page and the link to it are only accessible by
 users with launchpad.Edit permission for the blueprint.
 
   >>> user_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> user_browser.getLink('Remove dependency')
   Traceback (most recent call last):
   ...
   LinkNotFoundError
   >>> user_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/canvas/+removedependency')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/canvas/+removedependency')
   Traceback (most recent call last):
   ...
   Unauthorized: ...
@@ -126,20 +126,20 @@
 show this, we register a blueprint for a different project.
 
   >>> owner_browser.open(
-  ...     'http://blueprints.launchpad.dev/jokosher/+addspec')
+  ...     'http://blueprints.launchpad.test/jokosher/+addspec')
   >>> owner_browser.getControl('Name').value = 'test-blueprint'
   >>> owner_browser.getControl('Title').value = 'Test Blueprint'
   >>> owner_browser.getControl('Summary').value = (
   ...     'Another blueprint in a different project')
   >>> owner_browser.getControl('Register Blueprint').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/jokosher/+spec/test-blueprint'
+  'http://blueprints.launchpad.test/jokosher/+spec/test-blueprint'
 
 We then try to make the canvas blueprint in firefox depend on the
 blueprint we registered in jokosher.
 
   >>> owner_browser.open(
-  ...     'http://blueprints.launchpad.dev/firefox/'
+  ...     'http://blueprints.launchpad.test/firefox/'
   ...     '+spec/canvas/+linkdependency')
   >>> owner_browser.getControl(
   ...     'Depends On').value = 'test-blueprint'
@@ -158,7 +158,7 @@
 "canvas".
 
   >>> owner_browser.open(
-  ...     'http://blueprints.launchpad.dev/firefox/+spec/e4x/+linkdependency')
+  ...     'http://blueprints.launchpad.test/firefox/+spec/e4x/+linkdependency')
   >>> owner_browser.getControl(
   ...     'Depends On').value = 'canvas'
   >>> owner_browser.getControl('Continue').click()
@@ -172,24 +172,24 @@
 regardless of its status. Let's mark mergewin as Implemented:
 
   >>> owner_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/mergewin')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/mergewin')
   >>> owner_browser.getLink(url='+status').click()
   >>> owner_browser.getControl(
   ...     'Implementation Status').value = ['IMPLEMENTED']
   >>> owner_browser.getControl('Change').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/mergewin'
+  'http://blueprints.launchpad.test/firefox/+spec/mergewin'
 
 And ensure it works:
 
   >>> owner_browser.open(
-  ...   'http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  ...   'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> owner_browser.getLink('Add dependency').click()
   >>> owner_browser.getControl(
   ...     'Depends On').value = 'mergewin'
   >>> owner_browser.getControl('Continue').click()
   >>> owner_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 
 == Project dependency charts ==
@@ -201,7 +201,7 @@
 implemented. The "dependency tree" page for "canvas" should show exactly
 that.
 
-  >>> anon_browser.open('http://launchpad.dev/firefox/+spec/canvas/+deptree')
+  >>> anon_browser.open('http://launchpad.test/firefox/+spec/canvas/+deptree')
   >>> print '----'; print anon_browser.contents
   ----
   ...Blueprints that must be implemented first...
@@ -217,25 +217,25 @@
 image and map.
 
   >>> anon_browser.open(
-  ...   'http://launchpad.dev/firefox/+spec/canvas/+deptreeimgtag')
+  ...   'http://launchpad.test/firefox/+spec/canvas/+deptreeimgtag')
   >>> print anon_browser.contents
   <img src="deptree.png" usemap="#deptree" />
   <map id="deptree" name="deptree">
   <area shape="poly"
     ...title="Support &lt;canvas&gt; Objects" .../>
   <area shape="poly"
-    ...href="http://blueprints.launchpad.dev/firefox/+spec/e4x"; .../>
-  <area shape="poly"
-    ...href="http://blueprints.launchpad.dev/firefox/+spec/mergewin"; .../>
-  <area shape="poly"
-    ...href="http://blueprints.launchpad.dev/firefox/+spec/svg...support"; .../>
+    ...href="http://blueprints.launchpad.test/firefox/+spec/e4x"; .../>
+  <area shape="poly"
+    ...href="http://blueprints.launchpad.test/firefox/+spec/mergewin"; .../>
+  <area shape="poly"
+    ...href="http://blueprints.launchpad.test/firefox/+spec/svg...support"; .../>
   </map>
 
 
 Get the dependency chart, and check that it is a PNG.
 
   >>> anon_browser.open(
-  ...   'http://launchpad.dev/firefox/+spec/canvas/deptree.png')
+  ...   'http://launchpad.test/firefox/+spec/canvas/deptree.png')
   >>> anon_browser.contents.startswith('\x89PNG')
   True
   >>> anon_browser.headers['content-type']
@@ -245,7 +245,7 @@
 is useful for experimenting with the dot layout using production data.
 
   >>> anon_browser.open(
-  ...   'http://launchpad.dev/firefox/+spec/canvas/+deptreedotfile')
+  ...   'http://launchpad.test/firefox/+spec/canvas/+deptreedotfile')
   >>> anon_browser.headers['content-type']
   'text/plain;charset=utf-8'
   >>> print anon_browser.contents
@@ -257,16 +257,16 @@
 Let's look at blueprints targetting a distribution, rather than a product.
 We create two blueprints in `ubuntu`.
 
-  >>> owner_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+  >>> owner_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
   >>> owner_browser.getControl('Name').value = 'distro-blueprint-a'
   >>> owner_browser.getControl('Title').value = 'A blueprint for a distro'
   >>> owner_browser.getControl('Summary').value = (
   ...     'This is a blueprint for the Ubuntu distribution')
   >>> owner_browser.getControl('Register Blueprint').click()
   >>> print owner_browser.url
-  http://blueprints.launchpad.dev/ubuntu/+spec/distro-blueprint-a
+  http://blueprints.launchpad.test/ubuntu/+spec/distro-blueprint-a
 
-  >>> owner_browser.open('http://blueprints.launchpad.dev/ubuntu/+addspec')
+  >>> owner_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
   >>> owner_browser.getControl('Name').value = 'distro-blueprint-b'
   >>> owner_browser.getControl('Title').value = (
   ...     'Another blueprint for a distro')
@@ -274,7 +274,7 @@
   ...     'This is a blueprint for the Ubuntu distribution')
   >>> owner_browser.getControl('Register Blueprint').click()
   >>> print owner_browser.url
-  http://blueprints.launchpad.dev/ubuntu/+spec/distro-blueprint-b
+  http://blueprints.launchpad.test/ubuntu/+spec/distro-blueprint-b
 
   >>> owner_browser.getLink('Add dependency').click()
   >>> print owner_browser.url

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt'
--- lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt	2010-08-26 02:30:06 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt	2019-05-22 15:20:07 +0000
@@ -8,10 +8,10 @@
 
   >>> user_browser = browser
   >>> user_browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
-  >>> url = 'http://blueprints.launchpad.dev/ubuntu/+addspec'
+  >>> url = 'http://blueprints.launchpad.test/ubuntu/+addspec'
   >>> user_browser.open(url)
   >>> user_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/+addspec'
+  'http://blueprints.launchpad.test/ubuntu/+addspec'
 
 Then we try to add a specification to that distro
 
@@ -29,35 +29,35 @@
 We're redirected to the Specification page
 
   >>> user_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/+spec/testspec'
+  'http://blueprints.launchpad.test/ubuntu/+spec/testspec'
 
 Now we try to open the +setdistroseries page, where there is a form to
 target the newly created spec to a distribution.
 
 
-  >>> url = 'http://blueprints.launchpad.dev/ubuntu/+spec/testspec/+setdistroseries'
+  >>> url = 'http://blueprints.launchpad.test/ubuntu/+spec/testspec/+setdistroseries'
   >>> user_browser.open(url)
   >>> user_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/+spec/testspec/+setdistroseries'
+  'http://blueprints.launchpad.test/ubuntu/+spec/testspec/+setdistroseries'
 
 The page contains a link back to the blueprint, in case you change your mind.
 
     >>> back_link = browser.getLink('Test Specification')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/testspec'
+    'http://blueprints.launchpad.test/ubuntu/+spec/testspec'
     >>> browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/testspec'
+    'http://blueprints.launchpad.test/ubuntu/+spec/testspec'
 
 We are able to target a specification to a distroseries. We expect to be
 redirected back to the spec page when we are done.
 
-  >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/+spec/media-integrity-check/+setdistroseries')
+  >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/+spec/media-integrity-check/+setdistroseries')
   >>> user_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/+spec/media-integrity-check/+setdistroseries'
+  'http://blueprints.launchpad.test/ubuntu/+spec/media-integrity-check/+setdistroseries'
   >>> user_browser.getControl('Goal').value = ['5']
   >>> user_browser.getControl('Continue').click()
   >>> user_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/+spec/media-integrity-check'
+  'http://blueprints.launchpad.test/ubuntu/+spec/media-integrity-check'
  
 
 After the POST we should see the goal on the specification page, as
@@ -82,19 +82,19 @@
 
 However, we can expect to find it on the approvals page.
 
-  >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/grumpy/+specs')
+  >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy/+specs')
   >>> "CD Media Integrity Check" in user_browser.contents
   False
 
 We will accept it:
 
-  >>> admin_browser.open('http://blueprints.launchpad.dev/ubuntu/grumpy/+setgoals')
+  >>> admin_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy/+setgoals')
   >>> 'CD Media Integrity' in admin_browser.contents
   True
   >>> admin_browser.getControl('CD Media Integrity Check').selected = True
   >>> admin_browser.getControl('Accept').click()
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/ubuntu/grumpy'
+  'http://blueprints.launchpad.test/ubuntu/grumpy'
   >>> 'Accepted 1 specification(s)' in admin_browser.contents
   True
 

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-editing.txt'
--- lib/lp/blueprints/stories/blueprints/xx-editing.txt	2011-09-02 04:04:46 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-editing.txt	2019-05-22 15:20:07 +0000
@@ -7,18 +7,18 @@
 First, we need to load the +edit page.
 
     >>> browser.addHeader('Authorization', 'Basic carlos@xxxxxxxxxxxxx:test')
-    >>> features_domain = 'http://blueprints.launchpad.dev'
+    >>> features_domain = 'http://blueprints.launchpad.test'
     >>> spec_path = '/firefox/+spec/extension-manager-upgrades'
     >>> browser.open(features_domain + spec_path)
     >>> browser.getLink('Change details').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades/+edit'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades/+edit'
 
 The page links back to the blueprint page, in case we change our minds.
 
     >>> back_link = browser.getLink('Extension Manager Upgrades')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
 
 Launchpad won't let us use an URL already used in another blueprint.
 
@@ -61,24 +61,24 @@
     >>> browser.getControl('Status Whiteboard').value = 'XXX'
     >>> browser.getControl('Change').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
 
 Also, we would like to assign these to someone other than Carlos, and we
 would also like to have a drafter associated with it.
 
     >>> browser.getLink(url='+people').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades/+people'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades/+people'
     >>> back_link = browser.getLink('Extension Manager System Upgrades')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
     >>> browser.getControl('Assignee').value = 'tsukimi@xxxxxxxxxx'
     >>> browser.getControl('Drafter').value = 'daf@xxxxxxxxxxxxx'
     >>> browser.getControl('Approver').value = 'stuart.bishop@xxxxxxxxxxxxx'
     >>> browser.getControl('Status Whiteboard').value = 'YYY'
     >>> browser.getControl('Change').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
 
 Finally, we should be able to change the status metadata (definition status,
 implementation status, estimated man days etc) of the specification.
@@ -86,29 +86,29 @@
     >>> browser.getLink(url='+status').click()
     >>> back_link = browser.getLink('Extension Manager System Upgrades')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
     >>> browser.getControl('Definition Status').value = ['DRAFT']
     >>> # browser.getControl('Estimated Developer Days').value = '5'
     >>> browser.getControl('Implementation Status').value = ['SLOW']
     >>> browser.getControl('Status Whiteboard').value = 'XXX'
     >>> browser.getControl('Change').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
 
 Any logged in user can edit a specification whiteboard.
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/kubuntu/'
+    ...     'http://blueprints.launchpad.test/kubuntu/'
     ...     '+spec/krunch-desktop-plan')
     >>> user_browser.getLink(url='+whiteboard').click()
     >>> back_link = user_browser.getLink('The Krunch Desktop Plan')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/kubuntu/+spec/krunch-desktop-plan'
+    'http://blueprints.launchpad.test/kubuntu/+spec/krunch-desktop-plan'
 
     >>> user_browser.getControl('Whiteboard').value = 'XXX by Sample Person'
     >>> user_browser.getControl('Change').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/kubuntu/+spec/krunch-desktop-plan'
+    'http://blueprints.launchpad.test/kubuntu/+spec/krunch-desktop-plan'
 
     >>> 'XXX by Sample Person' in user_browser.contents
     True
@@ -121,7 +121,7 @@
     LinkNotFoundError
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/kubuntu/'
+    ...     'http://blueprints.launchpad.test/kubuntu/'
     ...     '+spec/krunch-desktop-plan/+status')
     Traceback (most recent call last):
     ...
@@ -130,7 +130,7 @@
 Nor can they change a blueprint's priority.
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/kubuntu/'
+    ...     'http://blueprints.launchpad.test/kubuntu/'
     ...     '+spec/krunch-desktop-plan')
     >>> user_browser.getLink(url='+priority')
     Traceback (most recent call last):
@@ -140,11 +140,11 @@
 But an administrator can.
 
     >>> admin_browser.open(
-    ...     'http://blueprints.launchpad.dev/kubuntu/'
+    ...     'http://blueprints.launchpad.test/kubuntu/'
     ...     '+spec/krunch-desktop-plan')
     >>> admin_browser.getLink(url='+priority').click()
     >>> admin_browser.url
-    'http://blueprints.launchpad.dev/kubuntu/+spec/krunch-desktop-plan/+priority'
+    'http://blueprints.launchpad.test/kubuntu/+spec/krunch-desktop-plan/+priority'
     >>> back_link = admin_browser.getLink('The Krunch Desktop Plan')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/kubuntu/+spec/krunch-desktop-plan'
+    'http://blueprints.launchpad.test/kubuntu/+spec/krunch-desktop-plan'

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-milestones.txt'
--- lib/lp/blueprints/stories/blueprints/xx-milestones.txt	2013-04-11 00:51:46 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-milestones.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 Let's target a blueprint to the 1.0 milestone of Mozilla. Before we do this,
 the milestone page lists one feature targeted already, and no bugs:
 
-    >>> admin_browser.open('http://launchpad.dev/firefox/+milestone/1.0')
+    >>> admin_browser.open('http://launchpad.test/firefox/+milestone/1.0')
     >>> tag = find_tag_by_id(admin_browser.contents, 'specification-count')
     >>> print extract_text(tag)
     1 blueprint
@@ -13,14 +13,14 @@
 milestone targeting.
 
   >>> admin_browser.open(
-  ...     'http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  ...     'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> admin_browser.getLink('Target milestone').click()
   >>> print admin_browser.title
   Target to a milestone : Support <canvas> Objects :
   Blueprints : Mozilla Firefox
   >>> back_link = admin_browser.getLink('Support <canvas> Objects')
   >>> back_link.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 Now, we choose a milestone from the list.
 
@@ -33,7 +33,7 @@
 We expect to be redirected to the spec home page.
 
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+  'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 And on that page, we expect to see that the spec is targeted to the 1.0
 milestone.
@@ -43,11 +43,11 @@
   <.../firefox/+milestone/1.0...
 
   >>> print admin_browser.getLink('1.0').url
-  http://launchpad.dev/firefox/+milestone/1.0
+  http://launchpad.test/firefox/+milestone/1.0
 
   >>> admin_browser.getLink('1.0').click()
   >>> print admin_browser.getLink('Support <canvas> Objects').url
-  http://blueprints.launchpad.dev/firefox/+spec/canvas
+  http://blueprints.launchpad.test/firefox/+spec/canvas
 
 The count of targeted features has also updated.
 

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt'
--- lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt	2010-10-21 18:06:05 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt	2019-05-22 15:20:07 +0000
@@ -1,17 +1,17 @@
 Non-ascii characters in specification titles are allowed.
 
   >>> admin_browser.open(
-  ...     'http://blueprints.launchpad.dev/firefox/+spec/e4x/+edit')
+  ...     'http://blueprints.launchpad.test/firefox/+spec/e4x/+edit')
 
   >>> admin_browser.getControl(
   ...     'Title').value = 'A title with non-ascii characters áã'
   >>> admin_browser.getControl('Change').click()
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/e4x'
+  'http://blueprints.launchpad.test/firefox/+spec/e4x'
 
 And they're correctly displayed in the dependency graph imagemap.
 
-  >>> anon_browser.open('http://launchpad.dev/firefox/+spec/canvas/+deptreeimgtag')
+  >>> anon_browser.open('http://launchpad.test/firefox/+spec/canvas/+deptreeimgtag')
   >>> print anon_browser.contents
   <img ...
   <map id="deptree" name="deptree">

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-productseries.txt'
--- lib/lp/blueprints/stories/blueprints/xx-productseries.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-productseries.txt	2019-05-22 15:20:07 +0000
@@ -23,13 +23,13 @@
     >>> browser.addHeader(
     ...     'Authorization', 'Basic celso.providelo@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
     >>> browser.getLink('Propose as goal').click()
     >>> back_link = browser.getLink('Support Native SVG Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
     >>> browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
 
 We can see two potential series candidates, the "trunk" and the "1.0" series.
 
@@ -47,7 +47,7 @@
   >>> print http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic celso.providelo@xxxxxxxxxxxxx:test
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Type: multipart/form-data; boundary=---------------------------26999413214087432371486976730
   ...
   ... -----------------------------26999413214087432371486976730
@@ -92,7 +92,7 @@
   >>> print http(r"""
   ... POST /firefox/+spec/e4x/+setproductseries HTTP/1.1
   ... Authorization: Basic celso.providelo@xxxxxxxxxxxxx:test
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Type: multipart/form-data; boundary=---------------------------26999413214087432371486976730
   ...
   ... -----------------------------26999413214087432371486976730
@@ -137,7 +137,7 @@
 
     >>> driver_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
     >>> driver_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/1.0/+setgoals')
+    ...     'http://blueprints.launchpad.test/firefox/1.0/+setgoals')
     >>> 'Support Native SVG' in driver_browser.contents
     True
     >>> driver_browser.getControl('Support Native SVG').selected = True
@@ -150,7 +150,7 @@
 there are none left in the queue.
 
     >>> driver_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/1.0/+setgoals')
+    ...     'http://blueprints.launchpad.test/firefox/1.0/+setgoals')
     >>> driver_browser.getControl('Support E4X').selected = True
     >>> driver_browser.getControl('Decline').click()
     >>> 'Declined 1 specification(s)' in driver_browser.contents
@@ -182,7 +182,7 @@
   >>> print http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Type: multipart/form-data; boundary=---------------------------26999413214087432371486976730
   ...
   ... -----------------------------26999413214087432371486976730
@@ -214,7 +214,7 @@
 OK, lets see if it was immediately accepted:
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://launchpad.test/firefox/+spec/svg-support')
     >>> 'firefox/trunk' in anon_browser.contents
     True
     >>> 'Accepted' in anon_browser.contents
@@ -225,7 +225,7 @@
   >>> print http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Type: multipart/form-data; boundary=---------------------------26999413214087432371486976730
   ...
   ... -----------------------------26999413214087432371486976730
@@ -256,7 +256,7 @@
 And again, it should be accepted automatically.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://launchpad.test/firefox/+spec/svg-support')
     >>> 'firefox/1.0' in anon_browser.contents
     True
     >>> 'Accepted' in anon_browser.contents

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-superseding-within-projects.txt'
--- lib/lp/blueprints/stories/blueprints/xx-superseding-within-projects.txt	2012-10-30 04:22:22 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-superseding-within-projects.txt	2019-05-22 15:20:07 +0000
@@ -13,19 +13,19 @@
 
     >>> browser.addHeader('Authorization', 'Basic foo.bar@xxxxxxxxxxxxx:test')
 
-    >>> browser.open('http://blueprints.launchpad.dev/bzr/+addspec')
+    >>> browser.open('http://blueprints.launchpad.test/bzr/+addspec')
     >>> browser.getControl('Name').value = 'a-unique-blueprint'
     >>> browser.getControl('Title').value = 'A unique Blueprint'
     >>> browser.getControl('Summary').value = 'A unique bzr Blueprint...'
     >>> browser.getControl('Register Blueprint').click()
 
-    >>> browser.open('http://blueprints.launchpad.dev/redfish/+addspec')
+    >>> browser.open('http://blueprints.launchpad.test/redfish/+addspec')
     >>> browser.getControl('Name').value = 'a-unique-blueprint'
     >>> browser.getControl('Title').value = 'A unique Blueprint'
     >>> browser.getControl('Summary').value = 'A unique refish Blueprint...'
     >>> browser.getControl('Register Blueprint').click()
 
-    >>> browser.open('http://blueprints.launchpad.dev/redfish/+addspec')
+    >>> browser.open('http://blueprints.launchpad.test/redfish/+addspec')
     >>> browser.getControl('Name').value = 'another-unique-blueprint'
     >>> browser.getControl('Title').value = 'Another unique Blueprint'
     >>> browser.getControl('Summary').value = 'Another refish Blueprint...'
@@ -35,7 +35,7 @@
 same project for which we know there is another blueprint in a different
 project with an identical name, which was registered earlier.
 
-    >>> browser.open('http://blueprints.launchpad.dev/redfish/' +
+    >>> browser.open('http://blueprints.launchpad.test/redfish/' +
     ...     '+spec/another-unique-blueprint/+supersede')
     >>> browser.getControl('Superseded by').value = 'a-unique-blueprint'
     >>> browser.getControl('Continue').click()
@@ -49,4 +49,4 @@
 
     >>> browser.getLink('A unique Blueprint').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/redfish/+spec/a-unique-blueprint'
+    'http://blueprints.launchpad.test/redfish/+spec/a-unique-blueprint'

=== modified file 'lib/lp/blueprints/stories/blueprints/xx-superseding.txt'
--- lib/lp/blueprints/stories/blueprints/xx-superseding.txt	2012-10-30 22:37:17 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-superseding.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic carlos@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/'
+    ...     'http://blueprints.launchpad.test/firefox/+spec/'
     ...     + 'extension-manager-upgrades')
     >>> 'New' in browser.contents
     True
@@ -18,7 +18,7 @@
 Make sure Bug 4116 stays fixed
 
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/'
+    ...     'http://blueprints.launchpad.test/firefox/+spec/'
     ...     + 'extension-manager-upgrades/+supersede'
     ...     )
 
@@ -27,9 +27,9 @@
 
     >>> back_link = browser.getLink('Extension Manager Upgrades')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
     >>> browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/firefox/+spec/extension-manager-upgrades'
+    'http://blueprints.launchpad.test/firefox/+spec/extension-manager-upgrades'
 
 Next, we will POST to that form, setting the spec which supersedes this one:
 

=== modified file 'lib/lp/blueprints/stories/sprints/sprint-settopics.txt'
--- lib/lp/blueprints/stories/sprints/sprint-settopics.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/blueprints/stories/sprints/sprint-settopics.txt	2019-05-22 15:20:07 +0000
@@ -1,7 +1,7 @@
 Any logged in user can propose specs to be discussed in a sprint.
 
   >>> user_browser.open(
-  ...     'http://blueprints.launchpad.dev/ubuntu/'
+  ...     'http://blueprints.launchpad.test/ubuntu/'
   ...     '+spec/media-integrity-check/+linksprint')
 
   >>> user_browser.getControl('Sprint').value = ['uds-guacamole']
@@ -11,7 +11,7 @@
   True
 
   >>> user_browser.open(
-  ...     'http://blueprints.launchpad.dev/kubuntu/'
+  ...     'http://blueprints.launchpad.test/kubuntu/'
   ...     '+spec/kde-desktopfile-langpacks/+linksprint')
   >>> user_browser.getControl('Sprint').value = ['uds-guacamole']
   >>> user_browser.getControl('Continue').click()
@@ -21,7 +21,7 @@
 
 Regular users can't approve items to be discussed in a sprint.
 
-  >>> user_browser.open('http://launchpad.dev/sprints/uds-guacamole')
+  >>> user_browser.open('http://launchpad.test/sprints/uds-guacamole')
   >>> user_browser.getLink('proposed')
   Traceback (most recent call last):
   ...
@@ -34,7 +34,7 @@
   LinkNotFoundError
 
   >>> user_browser.open(
-  ...     'http://launchpad.dev/sprints/uds-guacamole/+settopics')
+  ...     'http://launchpad.test/sprints/uds-guacamole/+settopics')
   Traceback (most recent call last):
   ...
   Unauthorized:...
@@ -43,16 +43,16 @@
 First choose a driver for the UDS Guacamole sprint.
 
   >>> browser = setupBrowser(auth='Basic mark@xxxxxxxxxxx:test')
-  >>> browser.open('http://launchpad.dev/sprints/uds-guacamole')
+  >>> browser.open('http://launchpad.test/sprints/uds-guacamole')
   >>> browser.getLink('Change details').click()
   >>> browser.url
-  'http://launchpad.dev/sprints/uds-guacamole/+edit'
+  'http://launchpad.test/sprints/uds-guacamole/+edit'
 
   >>> browser.getControl('Meeting Driver').value = 'ubuntu-team'
   >>> browser.getControl('Change').click()
 
   >>> browser.url
-  'http://launchpad.dev/sprints/uds-guacamole'
+  'http://launchpad.test/sprints/uds-guacamole'
 
   >>> meeting_drivers = find_tag_by_id(browser.contents, 'meeting-drivers')
   >>> print extract_text(meeting_drivers.findNext('a'))
@@ -62,10 +62,10 @@
 Guacamole agenda.
 
   >>> cprov_browser = setupBrowser(auth='Basic celso.providelo@xxxxxxxxxxxxx:test')
-  >>> cprov_browser.open('http://launchpad.dev/sprints/uds-guacamole')
+  >>> cprov_browser.open('http://launchpad.test/sprints/uds-guacamole')
   >>> cprov_browser.getLink('Blueprints').click()
   >>> cprov_browser.url
-  'http://blueprints.launchpad.dev/sprints/uds-guacamole'
+  'http://blueprints.launchpad.test/sprints/uds-guacamole'
   >>> cprov_browser.getLink('Set agenda').click()
 
   >>> print cprov_browser.title
@@ -82,6 +82,6 @@
   >>> cprov_browser.getControl('Decline').click()
 
   >>> cprov_browser.url
-  'http://blueprints.launchpad.dev/sprints/uds-guacamole/+specs'
+  'http://blueprints.launchpad.test/sprints/uds-guacamole/+specs'
 
 

=== modified file 'lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt'
--- lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt	2019-04-24 16:13:34 +0000
+++ lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt	2019-05-22 15:20:07 +0000
@@ -1,6 +1,6 @@
 Add some attendees to the sprint.
 
-    >>> admin_browser.open('http://launchpad.dev/sprints/ubz')
+    >>> admin_browser.open('http://launchpad.test/sprints/ubz')
     >>> admin_browser.getLink('Register yourself').click()
     >>> admin_browser.getControl('Register').click()
     >>> admin_browser.getLink('Register someone else').click()
@@ -9,7 +9,7 @@
 
 Get the sprint meeting export:
 
-    >>> browser.open('http://launchpad.dev/sprints/ubz/+temp-meeting-export')
+    >>> browser.open('http://launchpad.test/sprints/ubz/+temp-meeting-export')
     >>> print browser.headers['content-type']
     application/xml;charset=utf-8
 

=== modified file 'lib/lp/blueprints/stories/sprints/xx-sprints.txt'
--- lib/lp/blueprints/stories/sprints/xx-sprints.txt	2017-10-23 00:16:39 +0000
+++ lib/lp/blueprints/stories/sprints/xx-sprints.txt	2019-05-22 15:20:07 +0000
@@ -22,7 +22,7 @@
 
 Let's start by viewing the list of sprints registered.
 
-    >>> user_browser.open('http://launchpad.dev/sprints')
+    >>> user_browser.open('http://launchpad.test/sprints')
     >>> user_browser.title
     'Meetings and sprints registered in Launchpad'
 
@@ -41,7 +41,7 @@
 
     >>> user_browser.getLink('Rome').click()
     >>> user_browser.url
-    'http://launchpad.dev/sprints/rome'
+    'http://launchpad.test/sprints/rome'
 
 Creating new sprints
 ====================
@@ -49,7 +49,7 @@
 We should also be able to create a new sprint. We do this off the
 Sprints +new page.
 
-    >>> user_browser.open('http://launchpad.dev/sprints')
+    >>> user_browser.open('http://launchpad.test/sprints')
     >>> user_browser.getLink('Register a meeting').click()
 
     >>> print user_browser.title
@@ -126,7 +126,7 @@
     >>> user_browser.getControl('Add Sprint').click()
 
     >>> user_browser.url
-    'http://launchpad.dev/sprints/ltsponsteroids'
+    'http://launchpad.test/sprints/ltsponsteroids'
 
 Since the sprint's time zone was set to UTC, the dates are displayed in
 that time zone:
@@ -144,7 +144,7 @@
 
 Add a new sprint with a different time zone is also handled correctly.
 
-    >>> user_browser.open('http://launchpad.dev/sprints/+new')
+    >>> user_browser.open('http://launchpad.test/sprints/+new')
     >>> user_browser.getControl('Name').value = 'africa-sprint'
     >>> user_browser.getControl('Title').value = 'Africa Sprint'
     >>> summary = 'This is a sprint summary. Some words about the sprint'
@@ -158,7 +158,7 @@
     >>> user_browser.getControl('Add Sprint').click()
 
     >>> user_browser.url
-    'http://launchpad.dev/sprints/africa-sprint'
+    'http://launchpad.test/sprints/africa-sprint'
 
     >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))
     Starts: 09:15 SAST on Monday, 2006-07-10
@@ -169,7 +169,7 @@
 We should be able to edit the details on a sprint but the menus are only
 available to those who have permissions to edit that sprint.
 
-    >>> anon_browser.open('http://launchpad.dev/sprints/ubz')
+    >>> anon_browser.open('http://launchpad.test/sprints/ubz')
     >>> print anon_browser.title
     Ubuntu Below Zero : Meetings
 
@@ -182,7 +182,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
 
-    >>> browser.open('http://launchpad.dev/sprints/ubz')
+    >>> browser.open('http://launchpad.test/sprints/ubz')
     >>> print browser.title
     Ubuntu Below Zero : Meetings
 
@@ -191,9 +191,9 @@
     False
     >>> browser.getLink('Change details').click()
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz/+edit'
+    'http://launchpad.test/sprints/ubz/+edit'
     >>> browser.getLink('Cancel').url
-    'http://launchpad.dev/sprints/ubz'
+    'http://launchpad.test/sprints/ubz'
 
 The sprint start and end times are expressed to the nearest minute, and
 not the second:
@@ -229,7 +229,7 @@
     >>> browser.getControl('Change').click()
 
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz'
+    'http://launchpad.test/sprints/ubz'
 
 
 The address of the sprint is now visible.
@@ -245,11 +245,11 @@
 If we just change the time zone on the edit form, the start and finish
 dates will be changed too, since they follow local time:
 
-    >>> browser.open('http://launchpad.dev/sprints/ubz/+edit')
+    >>> browser.open('http://launchpad.test/sprints/ubz/+edit')
     >>> browser.getControl('Timezone').value = ['Australia/Darwin']
     >>> browser.getControl('Change').click()
     >>> print browser.url
-    http://launchpad.dev/sprints/ubz
+    http://launchpad.test/sprints/ubz
 
     >>> print extract_text(find_tag_by_id(browser.contents, 'start-end'))
     Starts: 08:30 ACST on Tuesday, 2006-01-10
@@ -258,7 +258,7 @@
 
 We should be able to see the workload of a sprint:
 
-    >>> anon_browser.open('http://launchpad.dev/sprints/ubz/+assignments')
+    >>> anon_browser.open('http://launchpad.test/sprints/ubz/+assignments')
     >>> print anon_browser.title
     Assignments : Blueprints : Ubuntu Below Zero : Meetings
 
@@ -279,7 +279,7 @@
 no spec assigned to people.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/sprints/ltsponsteroids/+assignments')
+    ...     'http://launchpad.test/sprints/ltsponsteroids/+assignments')
     >>> notice = find_tag_by_id(anon_browser.contents, 'no-blueprints')
     >>> print extract_text(notice)
     There are no open blueprints.
@@ -292,11 +292,11 @@
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
 
-    >>> browser.open('http://launchpad.dev/sprints/ubz')
+    >>> browser.open('http://launchpad.test/sprints/ubz')
 
     >>> browser.getLink('Register yourself').click()
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz/+attend'
+    'http://launchpad.test/sprints/ubz/+attend'
 
     >>> print browser.title
     Register your attendance : Ubuntu Below Zero : Meetings
@@ -327,7 +327,7 @@
     >>> browser.getControl('Register').click()
 
     >>> print browser.url
-    http://launchpad.dev/sprints/ubz/+attend
+    http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
     ...     print tag.renderContents()
@@ -341,7 +341,7 @@
     >>> browser.getControl('Register').click()
 
     >>> print browser.url
-    http://launchpad.dev/sprints/ubz/+attend
+    http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
     ...     print tag.renderContents()
@@ -357,7 +357,7 @@
     >>> browser.getControl('Register').click()
 
     >>> print browser.url
-    http://launchpad.dev/sprints/ubz/+attend
+    http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
     ...     print tag.renderContents()
@@ -373,7 +373,7 @@
     >>> browser.getControl('To').value = '2006-02-12 20:11:00'
     >>> browser.getControl('Register').click()
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz'
+    'http://launchpad.test/sprints/ubz'
 
 Now, Sample Person should be listed as an attendee.
 
@@ -398,10 +398,10 @@
 
 Also, it is possible to register someone else. Let's register Carlos.
 
-    >>> browser.open('http://launchpad.dev/sprints/ubz')
+    >>> browser.open('http://launchpad.test/sprints/ubz')
     >>> browser.getLink('Register someone else').click()
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz/+register'
+    'http://launchpad.test/sprints/ubz/+register'
 
 By default, the form is pre-filled with attendance times that match the
 start and end of the conference.
@@ -424,13 +424,13 @@
     >>> browser.getControl('Register').click()
 
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz'
+    'http://launchpad.test/sprints/ubz'
 
 Sample Person registers Salgado as well.
 
     >>> browser.getLink('Register someone else').click()
     >>> browser.url
-    'http://launchpad.dev/sprints/ubz/+register'
+    'http://launchpad.test/sprints/ubz/+register'
 
     >>> browser.getControl('Attendee').value = (
     ...     'guilherme.salgado@xxxxxxxxxxxxx')
@@ -472,12 +472,12 @@
     text/csv
 
     >>> carlos_browser = setupBrowser(auth='Basic carlos@xxxxxxxxxxxxx:test')
-    >>> carlos_browser.open('http://launchpad.dev/sprints/ubz')
+    >>> carlos_browser.open('http://launchpad.test/sprints/ubz')
     >>> carlos_browser.getLink('Export attendees to CSV').click()
     >>> print carlos_browser.headers['content-type']
     text/csv
 
-    >>> admin_browser.open('http://launchpad.dev/sprints/ubz')
+    >>> admin_browser.open('http://launchpad.test/sprints/ubz')
     >>> admin_browser.getLink('Export attendees to CSV').click()
     >>> print admin_browser.headers['content-type']
     text/csv
@@ -493,13 +493,13 @@
 
 Unregistered and anonymous users cannot access the CSV report.
 
-    >>> user_browser.open('http://launchpad.dev/sprints/ubz')
+    >>> user_browser.open('http://launchpad.test/sprints/ubz')
     >>> user_browser.getLink('Export attendees to CSV').click()
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
-    >>> anon_browser.open('http://launchpad.dev/sprints/ubz/+attendees-csv')
+    >>> anon_browser.open('http://launchpad.test/sprints/ubz/+attendees-csv')
     Traceback (most recent call last):
     ...
     Unauthorized:...
@@ -508,7 +508,7 @@
 physical or remote attendance, and the CSV report always reports such people
 as attending remotely.
 
-    >>> browser.open('http://launchpad.dev/sprints/ltsponsteroids')
+    >>> browser.open('http://launchpad.test/sprints/ltsponsteroids')
     >>> browser.getLink('Register yourself').click()
     >>> browser.getControl(name='field.is_physical')
     Traceback (most recent call last):

=== modified file 'lib/lp/blueprints/stories/standalone/sprint-links.txt'
--- lib/lp/blueprints/stories/standalone/sprint-links.txt	2017-04-10 10:49:19 +0000
+++ lib/lp/blueprints/stories/standalone/sprint-links.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 sample data. We will use Sample Person, who has no special privileges.
 
   >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
-  >>> browser.open('http://blueprints.launchpad.dev/firefox/+spec/canvas')
+  >>> browser.open('http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> browser.isHtml
   True
 
@@ -25,7 +25,7 @@
 
     >>> back_link = browser.getLink('Support <canvas> Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/canvas'
+    'http://blueprints.launchpad.test/firefox/+spec/canvas'
 
 Now  with a POST, we try to Add the spec to the Guacamole sprint.
 
@@ -77,7 +77,7 @@
     >>> logout()
 
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/'
+    ...     'http://blueprints.launchpad.test/'
     ...     'kubuntu/+spec/kde-desktopfile-langpacks')
     >>> 'Approved for the meeting agenda' in browser.contents
     False
@@ -104,7 +104,7 @@
   'http://.../kubuntu/+spec/kde-desktopfile-langpacks/rome'
   >>> back_link = browser.getLink('KDE Desktop File Language Packs')
   >>> back_link.url
-  'http://blueprints.launchpad.dev/kubuntu/+spec/kde-desktopfile-langpacks'
+  'http://blueprints.launchpad.test/kubuntu/+spec/kde-desktopfile-langpacks'
   >>> browser.getControl('Decline').click()
   >>> 'Declined for the meeting' not in browser.contents
   False

=== modified file 'lib/lp/blueprints/stories/standalone/subscribing.txt'
--- lib/lp/blueprints/stories/standalone/subscribing.txt	2017-10-23 00:16:39 +0000
+++ lib/lp/blueprints/stories/standalone/subscribing.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 
 First, let's make sure we can see the link called "Subscribe..."
 
-    >>> browser.open("http://blueprints.launchpad.dev/firefox/+spec/e4x";)
+    >>> browser.open("http://blueprints.launchpad.test/firefox/+spec/e4x";)
     >>> subscribe_link = browser.getLink('Subscribe')
     >>> subscribe_link is not None
     True
@@ -27,7 +27,7 @@
 
     >>> browser.addHeader('Authorization', 'Basic carlos@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     "http://blueprints.launchpad.dev/firefox/+spec/e4x/+subscribe";)
+    ...     "http://blueprints.launchpad.test/firefox/+spec/e4x/+subscribe";)
     >>> print browser.title
     Subscribe to blueprint : Support E4X in EcmaScript :
     Blueprints : Mozilla Firefox
@@ -36,7 +36,7 @@
 
     >>> back_link = browser.getLink('Support E4X in EcmaScript')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/e4x'
+    'http://blueprints.launchpad.test/firefox/+spec/e4x'
 
 There should be a control to set whether or not participation in
 discussions of this feature is essential. We will say we want to be
@@ -89,7 +89,7 @@
     >>> browser.getControl('Participation essential').selected = True
     >>> browser.getControl('Change').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/e4x'
+    'http://blueprints.launchpad.test/firefox/+spec/e4x'
 
     >>> 'subscriber-essential' in browser.contents
     True
@@ -117,11 +117,11 @@
 
 When we want other users to track a specification we can subscribe them.
 
-    >>> browser.open("http://blueprints.launchpad.dev/firefox/+spec/e4x";)
+    >>> browser.open("http://blueprints.launchpad.test/firefox/+spec/e4x";)
     >>> browser.getLink('Subscribe someone else').click()
     >>> back_link = browser.getLink('Support E4X in EcmaScript')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/e4x'
+    'http://blueprints.launchpad.test/firefox/+spec/e4x'
 
     >>> browser.getControl('Subscriber').value = 'stub'
     >>> browser.getControl('Subscribe').click()
@@ -150,7 +150,7 @@
 simply go through the process again, this time ticking the relevant
 checkbox.
 
-    >>> browser.open("http://blueprints.launchpad.dev/firefox/+spec/e4x";)
+    >>> browser.open("http://blueprints.launchpad.test/firefox/+spec/e4x";)
     >>> browser.getLink('Subscribe someone else').click()
     >>> browser.getControl('Subscriber').value = 'stub'
     >>> browser.getControl(name='field.essential').value = 'yes'
@@ -223,7 +223,7 @@
     >>> logout()
 
     >>> browser.open(
-    ...     "http://blueprints.launchpad.dev/";
+    ...     "http://blueprints.launchpad.test/";
     ...     "kubuntu/+spec/krunch-desktop-plan")
     >>> browser.getLink('Subscribe someone else').click()
     >>> browser.getControl('Subscriber').value = 'admins'
@@ -316,7 +316,7 @@
 not.
 
     >>> browser.open(
-    ...   "http://blueprints.launchpad.dev/firefox/+spec/svg-support";)
+    ...   "http://blueprints.launchpad.test/firefox/+spec/svg-support";)
     >>> subscribers = find_tags_by_class(browser.contents, 'subscriber')
     >>> for subscriber in subscribers:
     ...     a_tags = subscriber.findAll('a')

=== modified file 'lib/lp/blueprints/stories/standalone/xx-batching.txt'
--- lib/lp/blueprints/stories/standalone/xx-batching.txt	2015-06-27 04:10:49 +0000
+++ lib/lp/blueprints/stories/standalone/xx-batching.txt	2019-05-22 15:20:07 +0000
@@ -11,7 +11,7 @@
 To demonstrate this, we'll create a new project:
 
   >>> browser = user_browser
-  >>> browser.open("http://launchpad.dev/projects/+new";)
+  >>> browser.open("http://launchpad.test/projects/+new";)
   >>> browser.getControl('URL', index=0).value = 'big-project'
   >>> browser.getControl('Name').value = 'Big Project'
   >>> browser.getControl('Summary').value = 'A big project indeed.'
@@ -21,42 +21,42 @@
   >>> browser.getControl(name='field.license_info').value = 'foo'
   >>> browser.getControl('Complete Registration').click()
   >>> browser.url
-  'http://launchpad.dev/big-project'
+  'http://launchpad.test/big-project'
 
 In the beginning, a project hasn't had blueprints set up:
 
-  >>> browser.open("http://blueprints.launchpad.dev/big-project";)
+  >>> browser.open("http://blueprints.launchpad.test/big-project";)
   >>> print extract_text(find_main_content(browser.contents))
   Blueprints...does not know how...Configure Blueprints...
 
 But it's easy to change that.
   
-  >>> browser.open("http://blueprints.launchpad.dev/big-project/+configure-blueprints";)
+  >>> browser.open("http://blueprints.launchpad.test/big-project/+configure-blueprints";)
   >>> browser.getControl(name='field.blueprints_usage').value = ['LAUNCHPAD']
   >>> browser.getControl('Change').click()
   >>> browser.url
-  'http://blueprints.launchpad.dev/big-project'
+  'http://blueprints.launchpad.test/big-project'
 
 Initially the newly enabled feature has no blueprints.
 
-  >>> browser.open("http://blueprints.launchpad.dev/big-project";)
+  >>> browser.open("http://blueprints.launchpad.test/big-project";)
   >>> print extract_text(find_main_content(browser.contents))
   Blueprints...first blueprint in this project!...
   
 We'll go ahead and add just a single blueprint:
 
-  >>> browser.open('http://launchpad.dev/big-project/+addspec')
+  >>> browser.open('http://launchpad.test/big-project/+addspec')
   >>> browser.getControl('Name', index=0).value = 'blueprint-0'
   >>> browser.getControl('Title').value = 'Blueprint 0'
   >>> browser.getControl('Summary').value = 'Blueprint 0'
   >>> browser.getControl('Register Blueprint').click()
   >>> browser.url
-  'http://blueprints.launchpad.dev/big-project/+spec/blueprint-0'
+  'http://blueprints.launchpad.test/big-project/+spec/blueprint-0'
   
 When we ask for the complete list of blueprints for our project, the new 
 blueprint is listed:
 
-  >>> browser.open("http://blueprints.launchpad.dev/big-project";)
+  >>> browser.open("http://blueprints.launchpad.test/big-project";)
   >>> print extract_text(first_tag_by_class(browser.contents, 
   ...                                      'batch-navigation-index'))
   1...&rarr...1...of...1 result
@@ -64,7 +64,7 @@
 Let's add some more blueprints:
 
   >>> for index in range(1, 20):
-  ...     browser.open('http://launchpad.dev/big-project/+addspec')
+  ...     browser.open('http://launchpad.test/big-project/+addspec')
   ...     browser.getControl('Name', index=0).value = 'blueprint-%d' % index
   ...     browser.getControl('Title').value = 'Blueprint %d' % index
   ...     browser.getControl('Summary').value = 'Blueprint %d' % index
@@ -73,7 +73,7 @@
 Observe that now when we ask for the complete list of blueprints, only some of
 the blueprints are listed:
 
-  >>> browser.open("http://blueprints.launchpad.dev/big-project";)
+  >>> browser.open("http://blueprints.launchpad.test/big-project";)
   >>> print extract_text(first_tag_by_class(browser.contents, 
   ...                                      'batch-navigation-index'))
   1...&rarr...5...of...20 results

=== modified file 'lib/lp/blueprints/stories/standalone/xx-branch-links.txt'
--- lib/lp/blueprints/stories/standalone/xx-branch-links.txt	2013-10-15 01:43:35 +0000
+++ lib/lp/blueprints/stories/standalone/xx-branch-links.txt	2019-05-22 15:20:07 +0000
@@ -17,7 +17,7 @@
 If the user is not logged in, they will be asked to log in.
 
     >>> anon_browser.open(
-    ...     'http://code.launchpad.dev/~name12/firefox/main')
+    ...     'http://code.launchpad.test/~name12/firefox/main')
     >>> anon_browser.getLink('Link to a blueprint').click()
     Traceback (most recent call last):
     ...
@@ -37,7 +37,7 @@
 
     >>> browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
     >>> browser.open(
-    ...     'http://code.launchpad.dev/~name12/firefox/main')
+    ...     'http://code.launchpad.test/~name12/firefox/main')
     >>> printSpecBranchLinks(browser)
     No spec branch links
 
@@ -58,14 +58,14 @@
 the user to specify a branch to link to the blueprint.
 
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
     >>> browser.getLink('Link a related branch').click()
 
 There is a link back to the blueprint page, in case you change your mind.
 
     >>> back_link = browser.getLink('Support Native SVG Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
 
     >>> browser.getControl('Branch').value = '~mark/firefox/release-0.8'
     >>> browser.getControl('Continue').click()
@@ -73,7 +73,7 @@
 The blueprint page shows the linked branches in a portlet.
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
     >>> print extract_text(find_tag_by_id(user_browser.contents,
     ...     'linked_branches'))
     Related branches
@@ -100,13 +100,13 @@
     >>> browser.getLink(url='+branch/mark/firefox/release-0.8').click()
     >>> back_link = browser.getLink('Support Native SVG Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
     >>> browser.getControl('Delete').click()
 
 The branch is now no longer listed in the portlet:
 
     >>> user_browser.open(
-    ...     'http://blueprints.launchpad.dev/firefox/+spec/svg-support')
+    ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
     >>> print extract_text(find_tag_by_id(user_browser.contents,
     ...     'linked_branches'))
     Related branches

=== modified file 'lib/lp/blueprints/stories/standalone/xx-index.txt'
--- lib/lp/blueprints/stories/standalone/xx-index.txt	2011-05-27 13:22:47 +0000
+++ lib/lp/blueprints/stories/standalone/xx-index.txt	2019-05-22 15:20:07 +0000
@@ -21,7 +21,7 @@
 
 Users can see the latest blueprints available for the whole system:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/')
+    >>> user_browser.open('http://blueprints.launchpad.test/')
     >>> print extract_text(find_main_content(user_browser.contents))
     Blueprints
     All projects...
@@ -39,12 +39,12 @@
 For example, let's search for blueprints with the string 'svg' in their
 name and title, across all projects:
 
-    >>> browser.open('http://blueprints.launchpad.dev/')
+    >>> browser.open('http://blueprints.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = 'svg'
     >>> browser.getControl(name='field.scope').value = ['all']
     >>> browser.getControl(name='field.actions.search').click()
     >>> print browser.url
-    http://blueprints.launchpad.dev/?searchtext=svg
+    http://blueprints.launchpad.test/?searchtext=svg
 
 There is one such blueprint for the Firefox project, and it is listed
 among the results:
@@ -56,13 +56,13 @@
 We try the same search within the Firefox project only, expecting to
 find the same result:
 
-    >>> browser.open('http://blueprints.launchpad.dev/')
+    >>> browser.open('http://blueprints.launchpad.test/')
     >>> browser.getControl(name='field.search_text').value = 'svg'
     >>> browser.getControl(name='field.scope').value = ['project']
     >>> browser.getControl(name='field.scope.target').value = 'firefox'
     >>> browser.getControl(name='field.actions.search').click()
     >>> print browser.url
-    http://blueprints.launchpad.dev/firefox?searchtext=svg
+    http://blueprints.launchpad.test/firefox?searchtext=svg
     >>> print browser.getLink('svg-support').attrs['title']
     Support Native SVG Objects
 
@@ -77,11 +77,11 @@
 can expect the search to be performed over all projects, just like when
 selecting the `all` scope:
 
-    >>> browser.open('http://blueprints.launchpad.dev/specs?'
+    >>> browser.open('http://blueprints.launchpad.test/specs?'
     ...     'field.actions.search=Find+Blueprints&field.scope.target='
     ...     '&field.search_text=svg')
     >>> print browser.url
-    http://blueprints.launchpad.dev/?searchtext=svg
+    http://blueprints.launchpad.test/?searchtext=svg
     >>> print extract_text(find_tag_by_id(browser.contents, 'specs-table'))
     Support Native SVG Objects
     ...

=== modified file 'lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt'
--- lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt	2011-03-10 07:06:44 +0000
+++ lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt	2019-05-22 15:20:07 +0000
@@ -14,20 +14,20 @@
 
 We register a new blueprint.
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/jokosher/+addspec')
+    >>> user_browser.open('http://blueprints.launchpad.test/jokosher/+addspec')
     >>> user_browser.getControl('Name').value = 'informational-blueprint'
     >>> user_browser.getControl('Title').value = 'Informational blueprint'
     >>> user_browser.getControl('Summary').value = \
     ...     "A blueprint requiring no implementation."
     >>> user_browser.getControl('Register Blueprint').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/jokosher/+spec/informational-blueprint'
+    'http://blueprints.launchpad.test/jokosher/+spec/informational-blueprint'
 
 We then set the blueprint's implementation status to ''informational''.
 
     >>> user_browser.getLink(url='+status').click()
     >>> user_browser.url
-    'http://blueprints.launchpad.dev/jokosher/+spec/informational-blueprint/+status'
+    'http://blueprints.launchpad.test/jokosher/+spec/informational-blueprint/+status'
     >>> user_browser.getControl('Implementation Status').value = (
     ...     ['INFORMATIONAL'])
     >>> user_browser.getControl('Change').click()
@@ -46,7 +46,7 @@
     >>> user_browser.getLink(url='+status').click()
     >>> user_browser.getControl('Definition Status').value = ['APPROVED']
     >>> user_browser.getControl('Change').click()
-    >>> browser.open('http://blueprints.launchpad.dev/jokosher/+documentation')
+    >>> browser.open('http://blueprints.launchpad.test/jokosher/+documentation')
     >>> print extract_text(
     ...     find_tag_by_id(browser.contents, 'documentation-listing-table'))
     ...

=== modified file 'lib/lp/blueprints/stories/standalone/xx-overview.txt'
--- lib/lp/blueprints/stories/standalone/xx-overview.txt	2014-11-27 07:48:25 +0000
+++ lib/lp/blueprints/stories/standalone/xx-overview.txt	2019-05-22 15:20:07 +0000
@@ -33,7 +33,7 @@
 Let's use the Mozilla Firefox product as an example. Users can see the list
 of blueprints attached to Mozilla Firefox by following the "Blueprints" tab:
 
-    >>> user_browser.open('http://launchpad.dev/firefox')
+    >>> user_browser.open('http://launchpad.test/firefox')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
@@ -50,7 +50,7 @@
 series as an example. To begin with, there are no blueprints listed on
 the blueprints page for 1.0:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/firefox/1.0')
+    >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
     Blueprints for 1.0
@@ -59,7 +59,7 @@
 Let's target an existing Mozilla Firefox blueprint to the 1.0 series:
 
     >>> browser = admin_browser
-    >>> browser.open('http://launchpad.dev/firefox/+specs')
+    >>> browser.open('http://launchpad.test/firefox/+specs')
     >>> browser.getLink('svg-support').click()
     >>> print browser.title
     Support Native SVG Objects...
@@ -80,7 +80,7 @@
 We'll also target the blueprint to a milestone.  First we'll create a
 milestone:
 
-    >>> browser.open('http://launchpad.dev/firefox/1.0')
+    >>> browser.open('http://launchpad.test/firefox/1.0')
     >>> browser.getLink('Create milestone').click()
     >>> browser.getControl('Name').value = '1.0.9'
     >>> browser.getControl('Date Targeted').value = '2050-05-05'
@@ -89,7 +89,7 @@
 
 Now we'll target our chosen blueprint to the new milestone:
 
-    >>> browser.open('http://launchpad.dev/firefox/+specs')
+    >>> browser.open('http://launchpad.test/firefox/+specs')
     >>> browser.getLink('svg-support').click()
     >>> browser.getLink('Target milestone').click()
     >>> main = find_main_content(browser.contents)
@@ -110,7 +110,7 @@
 Now the blueprint listing for the 1.0 series includes an entry for our chosen
 blueprint. It also lists the milestone to which the blueprint is targeted:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/firefox/1.0')
+    >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
     Blueprints for 1.0...
@@ -130,7 +130,7 @@
 Let's use the Ubuntu distribution as an example. Users can see the list of
 blueprints attached to Ubuntu Linux by following the "Blueprints" tab:
 
-    >>> user_browser.open('http://launchpad.dev/ubuntu')
+    >>> user_browser.open('http://launchpad.test/ubuntu')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
@@ -147,7 +147,7 @@
 series as an example. To begin with, there are no blueprints listed on the
 blueprints page for Grumpy:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/grumpy')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
     Blueprints for Grumpy
@@ -156,7 +156,7 @@
 Let's target an existing Ubuntu blueprint to the Grumpy series:
 
     >>> browser = admin_browser
-    >>> browser.open('http://launchpad.dev/ubuntu/+specs')
+    >>> browser.open('http://launchpad.test/ubuntu/+specs')
     >>> browser.getLink('media-integrity-check').click()
     >>> main = find_main_content(browser.contents)
     >>> print browser.title
@@ -177,7 +177,7 @@
 We'll also target the blueprint to a milestone.  First we'll create a
 milestone:
 
-    >>> browser.open('http://launchpad.dev/ubuntu/grumpy/')
+    >>> browser.open('http://launchpad.test/ubuntu/grumpy/')
     >>> browser.getLink('Create milestone').click()
     >>> browser.getControl('Name').value = 'drift-1'
     >>> browser.getControl('Date Targeted').value = '2050-05-05'
@@ -186,7 +186,7 @@
 
 Now we'll target our chosen blueprint to the new milestone:
 
-    >>> browser.open('http://launchpad.dev/ubuntu/+specs')
+    >>> browser.open('http://launchpad.test/ubuntu/+specs')
     >>> browser.getLink('media-integrity-check').click()
     >>> browser.getLink('Target milestone').click()
     >>> print extract_text(find_main_content(browser.contents))
@@ -206,7 +206,7 @@
 Finally, the blueprint listing for Grumpy includes an entry for our chosen
 blueprint. It also lists the milestone to which the blueprint is targeted:
 
-    >>> user_browser.open('http://blueprints.launchpad.dev/ubuntu/grumpy')
+    >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
     >>> print extract_text(main).encode('ascii', 'backslashreplace')
     Blueprints for Grumpy...

=== modified file 'lib/lp/blueprints/stories/standalone/xx-personviews.txt'
--- lib/lp/blueprints/stories/standalone/xx-personviews.txt	2012-05-17 07:46:56 +0000
+++ lib/lp/blueprints/stories/standalone/xx-personviews.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 The Features link shows an overview of all the specifications
 involving that person.
 
-    >>> browser.open('http://blueprints.launchpad.dev/~name16')
+    >>> browser.open('http://blueprints.launchpad.test/~name16')
     >>> print browser.title
     Blueprints : Foo Bar
     >>> soup = find_main_content(browser.contents)
@@ -74,7 +74,7 @@
 for each member of the team, using batching.
 
     >>> from lp.services.helpers import backslashreplace
-    >>> browser.open('http://blueprints.launchpad.dev/~admins')
+    >>> browser.open('http://blueprints.launchpad.test/~admins')
     >>> print backslashreplace(browser.title)
     Blueprints : \u201cLaunchpad Administrators\u201d team
     >>> browser.getLink('Workload').click()

=== modified file 'lib/lp/blueprints/stories/standalone/xx-retargeting.txt'
--- lib/lp/blueprints/stories/standalone/xx-retargeting.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/blueprints/stories/standalone/xx-retargeting.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 
 First, load the svg-support spec on Firefox:
 
-  >>> admin_browser.open("http://launchpad.dev/firefox/+spec/svg-support";)
+  >>> admin_browser.open("http://launchpad.test/firefox/+spec/svg-support";)
 
 Now, let's make sure we can see the retargeting page for it, as the Foo Bar
 administrator:
@@ -16,16 +16,16 @@
 
     >>> back_link = admin_browser.getLink('Support Native SVG Objects')
     >>> back_link.url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
     >>> admin_browser.getLink('Cancel').url
-    'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+    'http://blueprints.launchpad.test/firefox/+spec/svg-support'
 
 We can move the blueprint to Evolution.
 
   >>> admin_browser.getControl("For").value = "evolution"
   >>> admin_browser.getControl("Retarget Blueprint").click()
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/evolution/+spec/svg-support'
+  'http://blueprints.launchpad.test/evolution/+spec/svg-support'
 
 OK. Now, it follows that we should be able to retarget it immediately from
 evolution, to a distribution. Let's try redhat.
@@ -34,7 +34,7 @@
   >>> admin_browser.getControl("For").value = "redhat"
   >>> admin_browser.getControl("Retarget Blueprint").click()
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/redhat/+spec/svg-support'
+  'http://blueprints.launchpad.test/redhat/+spec/svg-support'
 
 And similarly, this should now be on Red Hat, and we should be able to send
 it straight back to firefox. This means that the data set should finish this
@@ -44,7 +44,7 @@
   >>> admin_browser.getControl("For").value = "firefox"
   >>> admin_browser.getControl("Retarget Blueprint").click()
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/svg-support'
+  'http://blueprints.launchpad.test/firefox/+spec/svg-support'
 
 If we try to reassign the spec to a target which doesn't exist, we don't
 blow up:
@@ -56,7 +56,7 @@
 We stay on the same page and get an error message printed out:
 
   >>> admin_browser.url
-  'http://blueprints.launchpad.dev/firefox/+spec/svg-support/+retarget'
+  'http://blueprints.launchpad.test/firefox/+spec/svg-support/+retarget'
 
   >>> for tag in find_tags_by_class(admin_browser.contents, 'message'):
   ...     print tag.renderContents()

=== modified file 'lib/lp/blueprints/stories/standalone/xx-views.txt'
--- lib/lp/blueprints/stories/standalone/xx-views.txt	2011-03-10 07:08:59 +0000
+++ lib/lp/blueprints/stories/standalone/xx-views.txt	2019-05-22 15:20:07 +0000
@@ -170,31 +170,31 @@
 distribution:
 
     >>> browser = user_browser
-    >>> browser.open('http://blueprints.launchpad.dev/ubuntu')
+    >>> browser.open('http://blueprints.launchpad.test/ubuntu')
     >>> browser.getLink('Register a blueprint').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+addspec'
+    'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> browser.getControl('Name').value = 'blueprint-1'
     >>> browser.getControl('Title').value = 'The First Blueprint'
     >>> browser.getControl('Summary').value = 'The first blueprint.'
     >>> browser.getControl('Register').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/blueprint-1'
+    'http://blueprints.launchpad.test/ubuntu/+spec/blueprint-1'
     >>> browser.open(
-    ...     'http://blueprints.launchpad.dev/ubuntu/+addspec')
+    ...     'http://blueprints.launchpad.test/ubuntu/+addspec')
     >>> browser.getControl('Name').value = 'blueprint-2'
     >>> browser.getControl('Title').value = 'The Second Blueprint'
     >>> browser.getControl('Summary').value = 'The second blueprint.'
     >>> browser.getControl('Register').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/blueprint-2'
+    'http://blueprints.launchpad.test/ubuntu/+spec/blueprint-2'
 
 To begin with, we can see ''both'' blueprints by visiting the distribution's
 blueprints page:
 
     >>> browser.getLink('Blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu'
+    'http://blueprints.launchpad.test/ubuntu'
     >>> browser.getLink('blueprint-1')
     <Link...
     >>> browser.getLink('blueprint-2')
@@ -204,14 +204,14 @@
 
     >>> browser.getLink('blueprint-2').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/blueprint-2'
+    'http://blueprints.launchpad.test/ubuntu/+spec/blueprint-2'
     >>> browser.getLink(url='+status').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/blueprint-2/+status'
+    'http://blueprints.launchpad.test/ubuntu/+spec/blueprint-2/+status'
     >>> browser.getControl('Implementation Status').value = ['IMPLEMENTED']
     >>> browser.getControl('Change').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+spec/blueprint-2'
+    'http://blueprints.launchpad.test/ubuntu/+spec/blueprint-2'
 
 By default, implemented blueprints are ''not'' listed on a target's blueprint
 listing pages. So now when we visit the distribution's blueprints page, the
@@ -219,7 +219,7 @@
 
     >>> browser.getLink('Blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu'
+    'http://blueprints.launchpad.test/ubuntu'
     >>> browser.getLink('blueprint-1')
     <Link...
     >>> browser.getLink('blueprint-2')
@@ -232,7 +232,7 @@
 
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/+specs?show=all'
+    'http://blueprints.launchpad.test/ubuntu/+specs?show=all'
     >>> browser.getLink('blueprint-1')
     <Link...
     >>> browser.getLink('blueprint-2')
@@ -243,38 +243,38 @@
 
  * distribution series:
 
-    >>> browser.open('http://blueprints.launchpad.dev/ubuntu/hoary')
+    >>> browser.open('http://blueprints.launchpad.test/ubuntu/hoary')
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/ubuntu/hoary/+specs?show=all'
+    'http://blueprints.launchpad.test/ubuntu/hoary/+specs?show=all'
 
  * project groups:
 
-    >>> browser.open('http://blueprints.launchpad.dev/mozilla')
+    >>> browser.open('http://blueprints.launchpad.test/mozilla')
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/mozilla/+specs?show=all'
+    'http://blueprints.launchpad.test/mozilla/+specs?show=all'
 
  * products:
 
-    >>> browser.open('http://blueprints.launchpad.dev/firefox')
+    >>> browser.open('http://blueprints.launchpad.test/firefox')
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/+specs?show=all'
+    'http://blueprints.launchpad.test/firefox/+specs?show=all'
 
  * product series:
 
-    >>> browser.open('http://blueprints.launchpad.dev/firefox/1.0')
+    >>> browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/firefox/1.0/+specs?show=all'
+    'http://blueprints.launchpad.test/firefox/1.0/+specs?show=all'
 
  * projects:
 
-    >>> browser.open('http://blueprints.launchpad.dev/mozilla')
+    >>> browser.open('http://blueprints.launchpad.test/mozilla')
     >>> browser.getLink('List all blueprints').click()
     >>> browser.url
-    'http://blueprints.launchpad.dev/mozilla/+specs?show=all'
+    'http://blueprints.launchpad.test/mozilla/+specs?show=all'
     >>> specs = find_tag_by_id(browser.contents, 'speclisting')
     >>> print extract_text(specs)
     Priority Blueprint Design Delivery Assignee Project Series
@@ -290,13 +290,13 @@
   blueprint to a series.
 
     >>> browser = setupBrowser(auth='Basic foo.bar@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://blueprints.launchpad.dev/mozilla')
+    >>> browser.open('http://blueprints.launchpad.test/mozilla')
     >>> browser.getLink('svg-support').click()
     >>> browser.getLink('Propose as goal').click()
     >>> series_goal = browser.getControl('Series Goal')
     >>> series_goal.value = ['2']
     >>> browser.getControl('Continue').click()
-    >>> browser.open('http://blueprints.launchpad.dev/mozilla/+series/1.0')
+    >>> browser.open('http://blueprints.launchpad.test/mozilla/+series/1.0')
     >>> specs = find_tag_by_id(browser.contents, 'speclisting')
     >>> print extract_text(specs)
     Priority Blueprint Design Delivery Assignee Project Series Milestone

=== modified file 'lib/lp/blueprints/tests/test_webservice.py'
--- lib/lp/blueprints/tests/test_webservice.py	2017-10-25 13:10:41 +0000
+++ lib/lp/blueprints/tests/test_webservice.py	2019-05-22 15:20:07 +0000
@@ -426,7 +426,7 @@
         # Test canBeUnsubscribedByUser() API.
         webservice = LaunchpadWebServiceCaller(
             'launchpad-library', 'salgado-change-anything',
-            domain='api.launchpad.dev:8085')
+            domain='api.launchpad.test:8085')
 
         with person_logged_in(ANONYMOUS):
             db_spec = self.factory.makeSpecification()

=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
--- lib/lp/bugs/browser/tests/bug-views.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/browser/tests/bug-views.txt	2019-05-22 15:20:07 +0000
@@ -300,11 +300,11 @@
     [u'patch 1', u'patch 2']
     >>> [attachment['file'].http_url
     ...  for attachment in view.regular_attachments]
-    [u'http://bugs.launchpad.dev/firefox/+bug/5/+attachment/.../+files/a1',
-     u'http://bugs.launchpad.dev/firefox/+bug/5/+attachment/.../+files/a2']
+    [u'http://bugs.launchpad.test/firefox/+bug/5/+attachment/.../+files/a1',
+     u'http://bugs.launchpad.test/firefox/+bug/5/+attachment/.../+files/a2']
     >>> [patch['file'].http_url for patch in view.patches]
-    [u'http://bugs.launchpad.dev/firefox/+bug/5/+attachment/.../+files/p1',
-     u'http://bugs.launchpad.dev/firefox/+bug/5/+attachment/.../+files/p2']
+    [u'http://bugs.launchpad.test/firefox/+bug/5/+attachment/.../+files/p1',
+     u'http://bugs.launchpad.test/firefox/+bug/5/+attachment/.../+files/p2']
 
 
 Bug Navigation

=== modified file 'lib/lp/bugs/browser/tests/buglinktarget-views.txt'
--- lib/lp/bugs/browser/tests/buglinktarget-views.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/browser/tests/buglinktarget-views.txt	2019-05-22 15:20:07 +0000
@@ -32,7 +32,7 @@
     Link a bug report
 
     >>> print(view.cancel_url)
-    http://bugs.launchpad.dev/bugs/cve/2005-2730
+    http://bugs.launchpad.test/bugs/cve/2005-2730
 
 It has a simple widget to enter the bug number or nickname of the bug to link
 to. After it links the bug, it sends a ObjectModifiedEvent.
@@ -86,7 +86,7 @@
     Remove links to bug reports
 
     >>> print(view.cancel_url)
-    http://bugs.launchpad.dev/bugs/cve/2005-2730
+    http://bugs.launchpad.test/bugs/cve/2005-2730
 
 After removing the bugs, it sends a SQLObjectModified event.
 

=== modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
--- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt	2019-05-22 15:20:07 +0000
@@ -61,14 +61,14 @@
 filebug form so that it may be loaded asynchronously.
 
     >>> print(filebug_view.inline_filebug_form_url)
-    http://launchpad.dev/firefox/+filebug-inline-form
+    http://launchpad.test/firefox/+filebug-inline-form
 
 Similarly, the duplicate_search_url property returns the base URL for
 the duplicate search view, which can be used to load the list of
 possible duplicates for a bug asynchronously.
 
     >>> print(filebug_view.duplicate_search_url)
-    http://launchpad.dev/firefox/+filebug-show-similar
+    http://launchpad.test/firefox/+filebug-show-similar
 
 
 Adding tags to filed bugs

=== modified file 'lib/lp/bugs/browser/tests/person-bug-views.txt'
--- lib/lp/bugs/browser/tests/person-bug-views.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/browser/tests/person-bug-views.txt	2019-05-22 15:20:07 +0000
@@ -190,7 +190,7 @@
     >>> ubuntu_firefox_bugcounts['package_name']
     u'mozilla-firefox in Ubuntu'
     >>> ubuntu_firefox_bugcounts['package_search_url']
-    u'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox?field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
+    u'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox?field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
 
     >>> print(ubuntu_firefox_bugcounts['open_bugs_count'])
     1
@@ -202,13 +202,13 @@
     0
 
     >>> ubuntu_firefox_bugcounts['open_bugs_url']
-    u'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox?field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
+    u'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox?field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
     >>> ubuntu_firefox_bugcounts['critical_bugs_url']
-    u'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox?field.importance=Critical&field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
+    u'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox?field.importance=Critical&field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
     >>> ubuntu_firefox_bugcounts['unassigned_bugs_url']
-    u'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox?assignee_option=none&field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
+    u'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox?assignee_option=none&field.status=New&field.status=Incomplete&field.status=Confirmed&field.status=Triaged&field.status=In+Progress&field.status=Fix+Committed&search=Search'
     >>> ubuntu_firefox_bugcounts['inprogress_bugs_url']
-    u'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox?field.status=In+Progress&search=Search'
+    u'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox?field.status=In+Progress&search=Search'
 
 The total number of bugs, broken down in the same ways as the package
 bug counts, is also available.

=== modified file 'lib/lp/bugs/browser/tests/test_breadcrumbs.py'
--- lib/lp/bugs/browser/tests/test_breadcrumbs.py	2018-01-02 16:10:26 +0000
+++ lib/lp/bugs/browser/tests/test_breadcrumbs.py	2019-05-22 15:20:07 +0000
@@ -42,12 +42,12 @@
             bug=self.bug, owner=self.bug.owner,
             subject="test comment subject", body="test comment body")
         expected_breadcrumbs = [
-            ('Crumb Tester', 'http://launchpad.dev/crumb-tester'),
-            ('Bugs', 'http://bugs.launchpad.dev/crumb-tester'),
+            ('Crumb Tester', 'http://launchpad.test/crumb-tester'),
+            ('Bugs', 'http://bugs.launchpad.test/crumb-tester'),
             ('Bug #%s' % self.bug.id,
-             'http://bugs.launchpad.dev/crumb-tester/+bug/%s' % self.bug.id),
+             'http://bugs.launchpad.test/crumb-tester/+bug/%s' % self.bug.id),
             ('Comment #1',
-             'http://bugs.launchpad.dev/crumb-tester/+bug/%s/comments/1' % (
+             'http://bugs.launchpad.test/crumb-tester/+bug/%s/comments/1' % (
                 self.bug.id)),
             ]
         self.assertBreadcrumbs(expected_breadcrumbs, comment)

=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
--- lib/lp/bugs/browser/tests/test_bug_views.py	2019-04-24 16:13:34 +0000
+++ lib/lp/bugs/browser/tests/test_bug_views.py	2019-05-22 15:20:07 +0000
@@ -453,10 +453,10 @@
         self.assertFalse(cache_data['other_subscription_notifications'])
         subscription_data = cache_data['subscription']
         self.assertEqual(
-            'http://launchpad.dev/api/devel/bugs/%s' % bug.id,
+            'http://launchpad.test/api/devel/bugs/%s' % bug.id,
             subscription_data['bug_link'])
         self.assertEqual(
-            'http://launchpad.dev/api/devel/~%s' % person.name,
+            'http://launchpad.test/api/devel/~%s' % person.name,
             subscription_data['person_link'])
         self.assertEqual(
             'Discussion', subscription_data['bug_notification_level'])
@@ -630,7 +630,7 @@
         browser = self.getViewBrowser(bug, rootsite="bugs")
         # Hardcode this to be sure we've really got what we expected, with no
         # confusion about lp's own url generation machinery.
-        expected_url = 'http://bugs.launchpad.dev/bugs/%d' % bug.id
+        expected_url = 'http://bugs.launchpad.test/bugs/%d' % bug.id
         self.assertThat(
             browser.contents,
             HTMLContains(Tag(

=== modified file 'lib/lp/bugs/browser/tests/test_bugattachment_file_access.py'
--- lib/lp/bugs/browser/tests/test_bugattachment_file_access.py	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/browser/tests/test_bugattachment_file_access.py	2019-05-22 15:20:07 +0000
@@ -166,7 +166,7 @@
         self.assertEqual(303, response.status)
 
         # The Librarian URL has, for our test case, the form
-        # "https://NNNN.restricted.launchpad.dev:PORT/NNNN/foo.txt?token=...";
+        # "https://NNNN.restricted.launchpad.test:PORT/NNNN/foo.txt?token=...";
         # where NNNN and PORT are integers.
         parsed_url = urlparse(response.getHeader('location'))
         self.assertEqual('https', parsed_url.scheme)

=== modified file 'lib/lp/bugs/browser/tests/test_bugcomment.py'
--- lib/lp/bugs/browser/tests/test_bugcomment.py	2018-02-02 10:06:24 +0000
+++ lib/lp/bugs/browser/tests/test_bugcomment.py	2019-05-22 15:20:07 +0000
@@ -313,7 +313,7 @@
         bug_message = self.factory.makeBugComment()
         bugtask = bug_message.bugs[0].default_bugtask
         product = removeSecurityProxy(bugtask).target
-        url = 'http://bugs.launchpad.dev/%s/+bug/%s/comments/%s' % (
+        url = 'http://bugs.launchpad.test/%s/+bug/%s/comments/%s' % (
            product.name, bugtask.bug.id, 1)
         self.assertEqual(url, canonical_url(bug_message))
 

=== modified file 'lib/lp/bugs/browser/tests/test_buglisting.py'
--- lib/lp/bugs/browser/tests/test_buglisting.py	2015-10-15 14:09:50 +0000
+++ lib/lp/bugs/browser/tests/test_buglisting.py	2019-05-22 15:20:07 +0000
@@ -244,7 +244,7 @@
         default_bugtask_url = canonical_url(
             bug.default_bugtask, rootsite='bugs')
 
-        browser = self.getUserBrowser("http://bugs.launchpad.dev/";)
+        browser = self.getUserBrowser("http://bugs.launchpad.test/";)
         input_field = browser.getControl(name='field.searchtext')
         input_field.value = term_format_string.format(bug.id)
         browser.getControl(name='search').click()

=== modified file 'lib/lp/bugs/browser/tests/test_bugsupervisor.py'
--- lib/lp/bugs/browser/tests/test_bugsupervisor.py	2015-10-01 17:32:41 +0000
+++ lib/lp/bugs/browser/tests/test_bugsupervisor.py	2019-05-22 15:20:07 +0000
@@ -56,8 +56,8 @@
         adapter, context = view.adapters.popitem()
         self.assertEqual(BugSupervisorEditSchema, adapter)
         self.assertEqual(self.product, context)
-        self.assertEqual('http://launchpad.dev/boing', view.next_url)
-        self.assertEqual('http://launchpad.dev/boing', view.cancel_url)
+        self.assertEqual('http://launchpad.test/boing', view.next_url)
+        self.assertEqual('http://launchpad.test/boing', view.cancel_url)
 
     def test_owner_appoint_self_from_none(self):
         # This also verifies that displaynames are escaped.

=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_configure.py'
--- lib/lp/bugs/browser/tests/test_bugtarget_configure.py	2012-10-07 23:42:34 +0000
+++ lib/lp/bugs/browser/tests/test_bugtarget_configure.py	2019-05-22 15:20:07 +0000
@@ -51,8 +51,8 @@
             'bug_reporting_guidelines', 'bug_reported_acknowledgement',
             'enable_bugfiling_duplicate_search', 'bug_supervisor']
         self.assertEqual(fields, view.field_names)
-        self.assertEqual('http://launchpad.dev/boing', view.next_url)
-        self.assertEqual('http://launchpad.dev/boing', view.cancel_url)
+        self.assertEqual('http://launchpad.test/boing', view.next_url)
+        self.assertEqual('http://launchpad.test/boing', view.cancel_url)
 
     def test_bug_supervisor_view_attributes(self):
         login_person(self.bug_supervisor)
@@ -65,8 +65,8 @@
             'bug_reporting_guidelines', 'bug_reported_acknowledgement',
             'enable_bugfiling_duplicate_search']
         self.assertEqual(fields, view.field_names)
-        self.assertEqual('http://launchpad.dev/boing', view.next_url)
-        self.assertEqual('http://launchpad.dev/boing', view.cancel_url)
+        self.assertEqual('http://launchpad.test/boing', view.next_url)
+        self.assertEqual('http://launchpad.test/boing', view.cancel_url)
 
     def test_all_data_change(self):
         # Verify that the composed interface supports all fields.

=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2017-10-21 18:14:14 +0000
+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2019-05-22 15:20:07 +0000
@@ -861,7 +861,7 @@
         response = view.request.response
         self.assertEqual(302, response.getStatus())
         self.assertEqual(
-            'http://bugs.launchpad.dev/fnord/+filebug?'
+            'http://bugs.launchpad.test/fnord/+filebug?'
             'field.title=A+bug&'
             'field.tags=is+os',
             response.getHeader('Location'))
@@ -871,7 +871,7 @@
         view = self.makeProjectGroupFileBugView('fnord', u'caf\xe9', '')
         response = view.request.response
         self.assertEqual(
-            'http://bugs.launchpad.dev/fnord/+filebug?'
+            'http://bugs.launchpad.test/fnord/+filebug?'
             'field.title=caf%C3%A9&'
             'field.tags=',
             response.getHeader('Location'))

=== modified file 'lib/lp/bugs/browser/tests/test_bugtask_navigation.py'
--- lib/lp/bugs/browser/tests/test_bugtask_navigation.py	2014-10-22 18:38:16 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask_navigation.py	2019-05-22 15:20:07 +0000
@@ -54,9 +54,9 @@
         bug = self.factory.makeBug()
         product = self.factory.makeProduct()
         obj, view, request = test_traverse(
-            'http://api.launchpad.dev/1.0/%s/+bug/%d'
+            'http://api.launchpad.test/1.0/%s/+bug/%d'
             % (product.name, bug.default_bugtask.bug.id))
         self.assertEqual(
             removeSecurityProxy(view).target,
-            'http://api.launchpad.dev/1.0/%s/+bug/%d'
+            'http://api.launchpad.test/1.0/%s/+bug/%d'
             % (bug.default_bugtask.target.name, bug.default_bugtask.bug.id))

=== modified file 'lib/lp/bugs/browser/tests/test_cve.py'
--- lib/lp/bugs/browser/tests/test_cve.py	2018-11-12 16:04:10 +0000
+++ lib/lp/bugs/browser/tests/test_cve.py	2019-05-22 15:20:07 +0000
@@ -79,7 +79,7 @@
         html_data = self.view.render()
         cve_links = re.findall(
             r'<a style="text-decoration: none" '
-            r'href="http://bugs.launchpad.dev/bugs/cve/\d{4}-\d{4}";>'
+            r'href="http://bugs.launchpad.test/bugs/cve/\d{4}-\d{4}";>'
             r'<img src="/@@/link" alt="" />'
             r'<span style="text-decoration: underline">CVE-\d{4}-\d{4}</span>'
             r'</a>',
@@ -158,21 +158,21 @@
             [
                 {
                     'displayname': 'CVE-2011-0123',
-                    'url': 'http://bugs.launchpad.dev/bugs/cve/2011-0123',
+                    'url': 'http://bugs.launchpad.test/bugs/cve/2011-0123',
                     },
                 {
                     'displayname': 'CVE-2011-0456',
-                    'url': 'http://bugs.launchpad.dev/bugs/cve/2011-0456',
+                    'url': 'http://bugs.launchpad.test/bugs/cve/2011-0456',
                     },
                 ])
         expected = (
             '<a style="text-decoration: none" '
-            'href="http://bugs.launchpad.dev/bugs/cve/2011-0123";>'
+            'href="http://bugs.launchpad.test/bugs/cve/2011-0123";>'
             '<img src="/@@/link" alt="" />'
             '<span style="text-decoration: underline">CVE-2011-0123</span>'
             '</a><br />\n'
             '<a style="text-decoration: none" '
-            'href="http://bugs.launchpad.dev/bugs/cve/2011-0456";>'
+            'href="http://bugs.launchpad.test/bugs/cve/2011-0456";>'
             '<img src="/@@/link" alt="" />'
             '<span style="text-decoration: underline">CVE-2011-0456</span>'
             '</a>')

=== modified file 'lib/lp/bugs/doc/bug-change.txt'
--- lib/lp/bugs/doc/bug-change.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/bug-change.txt	2019-05-22 15:20:07 +0000
@@ -396,13 +396,13 @@
 
     >>> print(pretty(attachment_change.getBugActivity()))
     {'newvalue':
-         u'sample-attachment http://bugs.launchpad.dev/bugs/...+files/...',
+         u'sample-attachment http://bugs.launchpad.test/bugs/...+files/...',
      'oldvalue': None,
      'whatchanged': 'attachment added'}
 
     >>> print(attachment_change.getBugNotification()['text'])
     ** Attachment added: "sample-attachment"
-    http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/...
+    http://bugs.launchpad.test/bugs/.../+attachment/.../+files/...
 
 Or remove one.
 
@@ -414,12 +414,12 @@
     >>> print(pretty(attachment_change.getBugActivity()))
     {'newvalue': None,
      'oldvalue':
-         u'sample-attachment http://bugs.launchpad.dev/bugs/...+files/...',
+         u'sample-attachment http://bugs.launchpad.test/bugs/...+files/...',
      'whatchanged': 'attachment removed'}
 
     >>> print(attachment_change.getBugNotification()['text'])
     ** Attachment removed: "sample-attachment"
-    http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/...
+    http://bugs.launchpad.test/bugs/.../+attachment/.../+files/...
 
 
 == BugTaskAttributeChange ==

=== modified file 'lib/lp/bugs/doc/bug-export.txt'
--- lib/lp/bugs/doc/bug-export.txt	2011-11-25 04:25:00 +0000
+++ lib/lp/bugs/doc/bug-export.txt	2019-05-22 15:20:07 +0000
@@ -153,7 +153,7 @@
     <sender name="name12">Sample Person</sender>
     <date>...</date>
     <text>Added attachment</text>
-    <attachment href="http://bugs.launchpad.dev/bugs/4/.../+files/hello.txt";>
+    <attachment href="http://bugs.launchpad.test/bugs/4/.../+files/hello.txt";>
     <type>UNSPECIFIED</type>
     <filename>hello.txt</filename>
     <title>"Hello World" attachment</title>

=== modified file 'lib/lp/bugs/doc/bugnotification-email.txt'
--- lib/lp/bugs/doc/bugnotification-email.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/bugnotification-email.txt	2019-05-22 15:20:07 +0000
@@ -387,7 +387,7 @@
     ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
     ...     print("-----------------------------")
     ** Attachment added: "A screenshot of the problem"
-       http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/screenshot.png
+       http://bugs.launchpad.test/bugs/.../+attachment/.../+files/screenshot.png
     -----------------------------
 
 Removing an attachment generates a notification, too.
@@ -402,7 +402,7 @@
     ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
     ...     print("-----------------------------")
     ** Attachment removed: "A screenshot of the problem"
-       http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/screenshot.png
+       http://bugs.launchpad.test/bugs/.../+attachment/.../+files/screenshot.png
     -----------------------------
 
 Adding an attachment and marking it as a patch generates a different
@@ -421,7 +421,7 @@
     ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
     ...     print("-----------------------------")
     ** Patch added: "A new icon for the application"
-       http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/new-icon.png
+       http://bugs.launchpad.test/bugs/.../+attachment/.../+files/new-icon.png
     -----------------------------
 
 Removing a patch also generates a different notification.
@@ -436,7 +436,7 @@
     ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
     ...     print("-----------------------------")
     ** Patch removed: "A new icon for the application"
-       http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/new-icon.png
+       http://bugs.launchpad.test/bugs/.../+attachment/.../+files/new-icon.png
     -----------------------------
 
 

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2019-05-22 15:20:07 +0000
@@ -279,7 +279,7 @@
      '-- ',
      'You received this bug notification because you are subscribed to the bug',
      'report.',
-     'http://bugs.launchpad.dev/bugs/1',
+     'http://bugs.launchpad.test/bugs/1',
      '',
      'Title:',
      '  Firefox does not support SVG'...]
@@ -408,7 +408,7 @@
     X-Launchpad-Bug-Duplicate: 1
     <BLANKLINE>
     *** This bug is a duplicate of bug 1 ***
-        http://bugs.launchpad.dev/bugs/1
+        http://bugs.launchpad.test/bugs/1
     ...
     ----------------------------------------------------------------------
 
@@ -957,7 +957,7 @@
     --
     You received this bug notification because you are subscribed to the bug
     report.
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
     <BLANKLINE>
     Title:
       In the beginning...
@@ -985,7 +985,7 @@
     --
     You received this bug notification because you are a member of Verbose
     Team, which is subscribed to the bug report.
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
     <BLANKLINE>
     Title:
       In the beginning...
@@ -1005,7 +1005,7 @@
     --
     You received this bug notification because you are subscribed to the bug
     report.
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
     <BLANKLINE>
     Title:
       In the beginning...
@@ -1019,7 +1019,7 @@
        machinery. Ain't technology great?
     <BLANKLINE>
     To manage notifications about this bug go to:
-    http://bugs.launchpad.dev/.../+bug/.../+subscriptions
+    http://bugs.launchpad.test/.../+bug/.../+subscriptions
     ----------------------------------------------------------------------
 
 And Concise Team Person does too, even though their team doesn't want them:
@@ -1036,7 +1036,7 @@
     --
     You received this bug notification because you are a member of Concise
     Team, which is subscribed to the bug report.
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
     <BLANKLINE>
     Title:
       In the beginning...
@@ -1050,7 +1050,7 @@
        machinery. Ain't technology great?
     <BLANKLINE>
     To manage notifications about this bug go to:
-    http://bugs.launchpad.dev/.../+bug/.../+subscriptions
+    http://bugs.launchpad.test/.../+bug/.../+subscriptions
     ----------------------------------------------------------------------
 
 It's important to note that the bug title and description are wrapped
@@ -1302,7 +1302,7 @@
     ...
     You received this bug notification because you are subscribed to
     mozilla-firefox in Ubuntu.
-    http://bugs.launchpad.dev/bugs/1
+    http://bugs.launchpad.test/bugs/1
     ...
     ----------------------------------------------------------------------
     To: mark@xxxxxxxxxxx
@@ -1383,7 +1383,7 @@
     --
     You received this bug notification because you are a member of
     Addressless Team, which is subscribed to the bug report.
-    http://bugs.launchpad.dev/bugs/1
+    http://bugs.launchpad.test/bugs/1
     ...
     ----------------------------------------------------------------------
     To: test@xxxxxxxxxxxxx
@@ -1418,7 +1418,7 @@
     ...
     You received this bug notification because you are subscribed to
     mozilla-firefox in Ubuntu.
-    http://bugs.launchpad.dev/bugs/1
+    http://bugs.launchpad.test/bugs/1
     ...
     ----------------------------------------------------------------------
     To: mark@xxxxxxxxxxx

=== modified file 'lib/lp/bugs/doc/bugtracker-tokens.txt'
--- lib/lp/bugs/doc/bugtracker-tokens.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/bugtracker-tokens.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
     >>> from lp.services.verification.interfaces.logintoken import (
     ...     ILoginTokenSet)
     >>> bugtracker_api = xmlrpclib.ServerProxy(
-    ...     'http://xmlrpc-private.launchpad.dev:8087/bugs',
+    ...     'http://xmlrpc-private.launchpad.test:8087/bugs',
     ...     transport=XMLRPCTestTransport())
 
     >>> token_string = bugtracker_api.newBugTrackerToken()
@@ -28,7 +28,7 @@
     ...     view_name='+bugtracker-handshake')
 
     >>> print(token_url)
-    http://launchpad.dev/token/.../+bugtracker-handshake
+    http://launchpad.test/token/.../+bugtracker-handshake
 
 Visiting the token's +bugtracker-handshake URL will result in an HTTP
 200 response if the token is valid.

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2019-05-22 15:20:07 +0000
@@ -689,14 +689,14 @@
     ...     bugzilla.xmlrpc_transport.auth_cookie)
 
     >>> bug_id = bug_watch.bug.id
-    >>> bug_url = 'http://bugs.launchpad.dev/bugs/xxx'
+    >>> bug_url = 'http://bugs.launchpad.test/bugs/xxx'
     >>> remote_bug = bug_watch.remotebug
     >>> transaction.commit()
 
     >>> bugzilla.xmlrpc_transport.print_method_calls = True
     >>> bugzilla.setLaunchpadBugId(remote_bug, bug_id, bug_url)
     CALLED Bug.update_see_also({'add':
-        ['http://bugs.launchpad.dev/bugs...'], 'ids': [1]})
+        ['http://bugs.launchpad.test/bugs...'], 'ids': [1]})
 
 BugzillaAPI.getLaunchpadBugId() will currently always return None due to
 bug 490267.

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2019-05-22 15:20:07 +0000
@@ -528,7 +528,7 @@
     ...     bugzilla.xmlrpc_transport.auth_cookie)
 
     >>> bugzilla.setLaunchpadBugId(
-    ...     1, 10, 'http://bugs.launchpad.dev/bugs/xxx')
+    ...     1, 10, 'http://bugs.launchpad.test/bugs/xxx')
     Using XML-RPC to generate token.
     CALLED Launchpad.login({'token': '...'})
     Successfully validated the token.

=== modified file 'lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt	2019-05-22 15:20:07 +0000
@@ -582,7 +582,7 @@
     >>> test_transport.expireCookie(test_transport.auth_cookie)
     >>> with trac.responses():
     ...     trac.setLaunchpadBugId(
-    ...         '3', 15, 'http://bugs.launchpad.dev/bugs/xxx')
+    ...         '3', 15, 'http://bugs.launchpad.test/bugs/xxx')
     Using XML-RPC to generate token.
     Successfully validated the token.
 
@@ -608,7 +608,7 @@
     BugNotFound: 12345
 
     >>> trac.setLaunchpadBugId(
-    ...     '12345', 1, 'http://bugs.launchpad.dev/bugs/xxx')
+    ...     '12345', 1, 'http://bugs.launchpad.test/bugs/xxx')
     Traceback (most recent call last):
       ...
     BugNotFound: 12345

=== modified file 'lib/lp/bugs/doc/initial-bug-contacts.txt'
--- lib/lp/bugs/doc/initial-bug-contacts.txt	2018-12-21 21:47:08 +0000
+++ lib/lp/bugs/doc/initial-bug-contacts.txt	2019-05-22 15:20:07 +0000
@@ -214,7 +214,7 @@
     <BLANKLINE>
     --
     Firefox does not support SVG
-    http://bugs.launchpad.dev/bugs/1
+    http://bugs.launchpad.test/bugs/1
     You received this bug notification because you
     are subscribed to pmount in Ubuntu.
 

=== modified file 'lib/lp/bugs/doc/malone-xmlrpc.txt'
--- lib/lp/bugs/doc/malone-xmlrpc.txt	2018-06-29 23:10:57 +0000
+++ lib/lp/bugs/doc/malone-xmlrpc.txt	2019-05-22 15:20:07 +0000
@@ -6,7 +6,7 @@
     >>> import xmlrpclib
     >>> from lp.testing.xmlrpc import XMLRPCTestTransport
     >>> filebug_api = xmlrpclib.ServerProxy(
-    ...     'http://test@xxxxxxxxxxxxx:test@xxxxxxxxxxxxxxxxxxxx/bugs/',
+    ...     'http://test@xxxxxxxxxxxxx:test@xxxxxxxxxxxxxxxxxxxxx/bugs/',
     ...     transport=XMLRPCTestTransport())
 
 
@@ -72,7 +72,7 @@
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
     >>> print(bug_url)
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
 
     >>> from zope.component import getUtility
     >>> from lp.bugs.interfaces.bug import IBugSet
@@ -99,7 +99,7 @@
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
     >>> print(bug_url)
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
 
     >>> bug = bugset.get(get_bug_id_from_url(bug_url))
 
@@ -126,7 +126,7 @@
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
     >>> print(bug_url)
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
 
     >>> login('test@xxxxxxxxxxxxx')
     >>> bug = bugset.get(get_bug_id_from_url(bug_url))
@@ -277,7 +277,7 @@
 These requests are all handled by the private xml-rpc server.
 
     >>> bugtracker_api = xmlrpclib.ServerProxy(
-    ...     'http://xmlrpc-private.launchpad.dev:8087/bugs',
+    ...     'http://xmlrpc-private.launchpad.test:8087/bugs',
     ...     transport=XMLRPCTestTransport())
 
     >>> token_string = bugtracker_api.newBugTrackerToken()

=== modified file 'lib/lp/bugs/feed/bug.py'
--- lib/lp/bugs/feed/bug.py	2017-05-17 06:45:49 +0000
+++ lib/lp/bugs/feed/bug.py	2019-05-22 15:20:07 +0000
@@ -301,7 +301,7 @@
     """Bug feeds for a generic search.
 
     Searches are of the form produced by an advanced bug search, e.g.
-    http://bugs.launchpad.dev/bugs/+bugs.atom?field.searchtext=&;
+    http://bugs.launchpad.test/bugs/+bugs.atom?field.searchtext=&;
         search=Search+Bug+Reports&field.scope=all&field.scope.target=
     """
 

=== modified file 'lib/lp/bugs/javascript/tests/test_async_comment_loading.js'
--- lib/lp/bugs/javascript/tests/test_async_comment_loading.js	2017-07-21 16:43:02 +0000
+++ lib/lp/bugs/javascript/tests/test_async_comment_loading.js	2019-05-22 15:20:07 +0000
@@ -115,7 +115,7 @@
          */
         test_load_more_comments_is_recursive: function() {
             var next_batch_url_div =
-                '<div id="next-batch-url">https://launchpad.dev/</div>';
+                '<div id="next-batch-url">https://launchpad.test/</div>';
             var more_comments_to_load_markup =
                 '<div>Here, have a comment. There are more where this came' +
                 'from</div>';

=== modified file 'lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html'
--- lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html	2017-07-21 14:06:38 +0000
+++ lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html	2019-05-22 15:20:07 +0000
@@ -93,8 +93,8 @@
                 </div>
             </div>
             <p style="display: none">
-                <a id="filebug-form-url" href="https://bugs.launchpad.dev/foo/+filebug-inline-form";></a>
-                <a id="duplicate-search-url" href="https://bugs.launchpad.dev/foo/+filebug-show-similar";></a>
+                <a id="filebug-form-url" href="https://bugs.launchpad.test/foo/+filebug-inline-form";></a>
+                <a id="duplicate-search-url" href="https://bugs.launchpad.test/foo/+filebug-show-similar";></a>
             </p>
         </div>
     </body>

=== modified file 'lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js'
--- lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js	2013-03-20 03:41:40 +0000
+++ lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js	2019-05-22 15:20:07 +0000
@@ -77,7 +77,7 @@
 
         tearDown: function() {
             Y.one('#filebug-form').set(
-                    'action', 'https://bugs.launchpad.dev/foo/+filebug');
+                    'action', 'https://bugs.launchpad.test/foo/+filebug');
         },
 
         /**
@@ -126,7 +126,7 @@
             this.config.yio.io.doAfter = function() {
                 // Check the expected io calls have been made.
                 Y.ArrayAssert.itemsAreEqual(
-                    ['https://bugs.launchpad.dev/' +
+                    ['https://bugs.launchpad.test/' +
                      'foo/+filebug-show-similar?title=foo'],
                     this.config.yio.calls);
                 // filebug container should be visible after the dup search
@@ -241,7 +241,7 @@
          */
         test_project_initial_filebug_form_action: function() {
             Y.Assert.areEqual(
-                'https://bugs.launchpad.dev/foo/+filebug',
+                'https://bugs.launchpad.test/foo/+filebug',
                 Y.one('#filebug-form').get('action'));
         }
 

=== modified file 'lib/lp/bugs/mail/tests/test_commands.py'
--- lib/lp/bugs/mail/tests/test_commands.py	2015-09-25 10:23:48 +0000
+++ lib/lp/bugs/mail/tests/test_commands.py	2019-05-22 15:20:07 +0000
@@ -339,7 +339,7 @@
         message = self.factory.makeSignedMessage(
             body='borked\n affects fnord',
             subject='title borked',
-            to_address='new@xxxxxxxxxxxxxxxxxx')
+            to_address='new@xxxxxxxxxxxxxxxxxxx')
         filealias = self.factory.makeLibraryFileAlias()
         command = BugEmailCommand('bug', ['new'])
         params, event = command.execute(message, filealias)

=== modified file 'lib/lp/bugs/mail/tests/test_handler.py'
--- lib/lp/bugs/mail/tests/test_handler.py	2018-01-02 16:10:26 +0000
+++ lib/lp/bugs/mail/tests/test_handler.py	2019-05-22 15:20:07 +0000
@@ -181,12 +181,12 @@
         # user back and ask that they use attachments instead.
         big_body_text = 'This is really big.' * 10000
         message = self.getFailureForMessage(
-            'new@xxxxxxxxxxxxxxxxxx', body=big_body_text)
+            'new@xxxxxxxxxxxxxxxxxxx', body=big_body_text)
         self.assertIn("The description is too long.", message)
 
     def test_bug_not_found(self):
         # Non-existent bug numbers result in an informative error.
-        message = self.getFailureForMessage('1234@xxxxxxxxxxxxxxxxxx')
+        message = self.getFailureForMessage('1234@xxxxxxxxxxxxxxxxxxx')
         self.assertIn(
             "There is no such bug in Launchpad: 1234", message)
 
@@ -200,7 +200,7 @@
         # Drop the notifications from celebrity_logged_in.
         pop_notifications()
         message = self.getFailureForMessage(
-            '4@xxxxxxxxxxxxxxxxxx',
+            '4@xxxxxxxxxxxxxxxxxxx',
             from_address=removeSecurityProxy(person.preferredemail).email)
         self.assertIs(None, message)
 
@@ -209,7 +209,7 @@
         with celebrity_logged_in('admin'):
             getUtility(IBugSet).get(4).setPrivate(
                 True, self.factory.makePerson())
-        message = self.getFailureForMessage('4@xxxxxxxxxxxxxxxxxx')
+        message = self.getFailureForMessage('4@xxxxxxxxxxxxxxxxxxx')
         self.assertIn(
             "There is no such bug in Launchpad: 4", message)
 
@@ -230,7 +230,7 @@
             msg = self.factory.makeSignedMessage(
                 body='borked\n affects fnord',
                 subject='subject borked',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug
@@ -254,7 +254,7 @@
             msg = self.factory.makeSignedMessage(
                 body='borked\n affects fnord',
                 subject='subject borked',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug
@@ -272,7 +272,7 @@
             msg = self.factory.makeSignedMessage(
                 body='borked\n assignee pting\n affects fnord',
                 subject='affects after assignee',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug
@@ -290,7 +290,7 @@
             msg = self.factory.makeSignedMessage(
                 body='unsecure\n security yes\n affects fnord\n tag ajax',
                 subject='unsecure code',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug
@@ -313,7 +313,7 @@
             msg = self.factory.makeSignedMessage(
                 body='bad thing\n security yes\n affects fnord',
                 subject='security issue',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug
@@ -335,7 +335,7 @@
             msg = self.factory.makeSignedMessage(
                 body='unsecure\n informationtype userdata\n affects fnord',
                 subject='unsecure code',
-                to_address='new@xxxxxxxxxxxxxxxxxx')
+                to_address='new@xxxxxxxxxxxxxxxxxxx')
             handler.process(msg, msg['To'])
         notification = self.getLatestBugNotification()
         bug = notification.bug

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-also-affects-distribution-default-values.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-also-affects-distribution-default-values.txt	2009-09-02 22:13:06 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-also-affects-distribution-default-values.txt	2019-05-22 15:20:07 +0000
@@ -6,7 +6,7 @@
 first source package found in the "Affects" list.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/tomcat/+bug/2')
+    ...     'http://launchpad.test/tomcat/+bug/2')
 
     >>> from lp.bugs.tests.bug import print_bug_affects_table
     >>> print_bug_affects_table(user_browser.contents)
@@ -18,7 +18,7 @@
 
     >>> user_browser.getLink(url='+distrotask').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/tomcat/+bug/2/+distrotask'
+    'http://bugs.launchpad.test/tomcat/+bug/2/+distrotask'
     >>> user_browser.getControl('Source Package').value
     'mozilla-firefox'
 

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2019-05-22 15:20:07 +0000
@@ -2,13 +2,13 @@
 
 The test browser does not support javascript
     >>> user_browser.open(
-    ...     'http://launchpad.dev/firefox/+bug/1/+choose-affected-product')
+    ...     'http://launchpad.test/firefox/+bug/1/+choose-affected-product')
     >>> find_link = user_browser.getLink('Find')
     >>> find_link.url
-    'http://launchpad.dev/firefox/+bug...'
+    'http://launchpad.test/firefox/+bug...'
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+affects-new-product')
+    ...     'http://bugs.launchpad.test/firefox/+bug/1/+affects-new-product')
     >>> user_browser.getControl('Bug URL').value = (
     ...     'http://bugs.foo.org/bugs/show_bug.cgi?id=42')
     >>> user_browser.getControl('Project name').value = 'The Foo Project'
@@ -29,7 +29,7 @@
     >>> from lp.testing.pages import strip_label
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/tomcat/+bug/2/+affects-new-product')
+    ...     'http://bugs.launchpad.test/tomcat/+bug/2/+affects-new-product')
     >>> print(user_browser.title)
     Register project affected by...
     >>> user_browser.getControl('Bug URL').value = (
@@ -99,7 +99,7 @@
 user and ask them to check if it's correct.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+affects-new-product')
+    ...     'http://bugs.launchpad.test/firefox/+bug/1/+affects-new-product')
     >>> user_browser.getControl('Bug URL').value = (
     ...     'http://foo.org/notabug.cgi?id=42')
     >>> user_browser.getControl('Project name').value = 'Foo Project'

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2019-05-22 15:20:07 +0000
@@ -5,14 +5,14 @@
     >>> browser.open("http://localhost/firefox/+bug/6";)
     >>> browser.getLink(url='+distrotask').click()
     >>> browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/6/+distrotask'
+    'http://bugs.launchpad.test/firefox/+bug/6/+distrotask'
 
 On this page we can add distribution task. Just in case we change our
 mind, there is a cancel link that points back to the bug page:
 
     >>> cancel_link = browser.getLink('Cancel')
     >>> cancel_link.url
-    'http://bugs.launchpad.dev/firefox/+bug/6'
+    'http://bugs.launchpad.test/firefox/+bug/6'
 
 We start by adding an Ubuntu task. Since Ubuntu uses Launchpad as its
 bug tracker, the task gets added and we return to the newly created
@@ -21,18 +21,18 @@
     >>> browser.getControl('Distribution').value = ['ubuntu']
     >>> browser.getControl('Continue').click()
     >>> browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bug/6'
+    'http://bugs.launchpad.test/ubuntu/+bug/6'
 
 It should not be possible to add the same distrotask again.
 
     >>> browser.getLink(url='+distrotask').click()
     >>> browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bug/6/+distrotask'
+    'http://bugs.launchpad.test/ubuntu/+bug/6/+distrotask'
 
     >>> browser.getControl('Distribution').value = ['ubuntu']
     >>> browser.getControl('Continue').click()
     >>> browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bug/6/+distrotask'
+    'http://bugs.launchpad.test/ubuntu/+bug/6/+distrotask'
 
     >>> print_feedback_messages(browser.contents)
     There is 1 error.
@@ -88,7 +88,7 @@
     >>> browser.open('http://localhost/firefox/+bug/1')
     >>> browser.getLink(url='+distrotask').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1/+distrotask
+    http://bugs.launchpad.test/firefox/+bug/1/+distrotask
 
     >>> browser.getControl(name='field.distribution').value = ['debian']
     >>> browser.getControl("Source Package Name").value = "alsa-utils"
@@ -96,7 +96,7 @@
     ...     'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=123')
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/debian/+source/alsa-utils/+bug/1
+    http://bugs.launchpad.test/debian/+source/alsa-utils/+bug/1
 
 If we try to add an Ubuntu task together with a bug watch we get an
 error, because Ubuntu uses Launchpad as its bug tracker
@@ -108,7 +108,7 @@
     ...     'https://bugzilla.mozilla.org/show_bug.cgi?id=84')
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/debian/+source/alsa-utils/+bug/1/+distrotask
+    http://bugs.launchpad.test/debian/+source/alsa-utils/+bug/1/+distrotask
 
     >>> print_feedback_messages(browser.contents)
     There is 1 error.
@@ -122,7 +122,7 @@
     >>> browser.getControl('URL').value = ''
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/alsa-utils/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/alsa-utils/+bug/1
 
 It's not possible to change a bugtask to a existing one.
 
@@ -145,7 +145,7 @@
     ...     ).value = 'pmount'
     >>> browser.getControl('Save Changes').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/pmount/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/pmount/+bug/1
 
 We want to make people aware of that they should link bugtasks to bug
 watches in order to get automatic status updates. So if we try to add a
@@ -166,7 +166,7 @@
     >>> browser.getControl('Source Package Name').value = 'pmount'
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/pmount/+bug/1/+distrotask
+    http://bugs.launchpad.test/ubuntu/+source/pmount/+bug/1/+distrotask
 
     >>> print_feedback_messages(browser.contents)
     Debian doesn't use Launchpad as its bug tracker. ...
@@ -182,7 +182,7 @@
 
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/pmount/+bug/1/+distrotask
+    http://bugs.launchpad.test/ubuntu/+source/pmount/+bug/1/+distrotask
 
     >>> print_feedback_messages(browser.contents)
     Debian doesn't use Launchpad as its bug tracker. ...
@@ -192,7 +192,7 @@
 
     >>> browser.getControl('Add Anyway').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/debian/+source/pmount/+bug/1
+    http://bugs.launchpad.test/debian/+source/pmount/+bug/1
 
     >>> print(browser.contents)
     <...
@@ -231,7 +231,7 @@
     >>> browser.getControl(name='field.product').value = other_product_name
     >>> browser.getControl('Continue').click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/proprietary-product/+bug/.../+choose-affected-product
+    http://bugs.launchpad.test/proprietary-product/+bug/.../+choose-affected-product
 
     >>> print_feedback_messages(browser.contents)
     There is 1 error.
@@ -252,7 +252,7 @@
 package is linked to the evolution upstream product.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/evolution/+bug/6')
+    ...     'http://launchpad.test/ubuntu/+source/evolution/+bug/6')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> user_browser.getControl('Project').value
     Traceback (most recent call last):
@@ -267,7 +267,7 @@
 
     >>> user_browser.getLink('Choose another project').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/6/+choose-affected-product?field.product=evolution
+    http://bugs.launchpad.test/ubuntu/+source/evolution/+bug/6/+choose-affected-product?field.product=evolution
 
     >>> user_browser.getControl('Project').value
     'evolution'
@@ -277,7 +277,7 @@
 
     >>> cancel_link = user_browser.getLink('Cancel')
     >>> print(cancel_link.url)
-    http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/6
+    http://bugs.launchpad.test/ubuntu/+source/evolution/+bug/6
 
 But we'll choose Thunderbird.
 
@@ -312,12 +312,12 @@
 
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/thunderbird/+bug/6
+    http://bugs.launchpad.test/thunderbird/+bug/6
 
 Let's add the evolution task as well.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/evolution/+bug/6')
+    ...     'http://launchpad.test/ubuntu/+source/evolution/+bug/6')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> print(user_browser.url)
     http://.../ubuntu/+source/evolution/+bug/6/+choose-affected-product
@@ -325,7 +325,7 @@
     >>> user_browser.getControl('Add to Bug Report').click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/evolution/+bug/6
+    http://bugs.launchpad.test/evolution/+bug/6
 
 
 Error messages
@@ -334,7 +334,7 @@
 If we try to add an upstream task without specifying a product:
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/+bug/3')
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/+bug/3')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> print(user_browser.url)
     http://.../debian/+source/mozilla-firefox/+bug/3/+choose-affected-product
@@ -366,13 +366,13 @@
 
     >>> search_link = user_browser.getLink('search for it')
     >>> print(search_link.url)
-    http://bugs.launchpad.dev/projects
+    http://bugs.launchpad.test/projects
 
 Since we don't restrict the input, the user can write anything, so we
 need to make sure that everything is quoted before displaying the input.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/+bug/3'
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/+bug/3'
     ...     '/+choose-affected-product')
 
     >>> user_browser.getControl('Project').value = b'N\xc3\xb6 Such Product&<>'
@@ -393,7 +393,7 @@
 white space in the bug URL it will be stripped.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/'
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/'
     ...     '+bug/3/+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'alsa-utils'
     >>> user_browser.getControl('Continue').click()
@@ -407,7 +407,7 @@
 the new bug watch.
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/alsa-utils/+bug/3
+    http://bugs.launchpad.test/alsa-utils/+bug/3
 
     >>> affects_table = find_tags_by_class(
     ...     user_browser.contents, 'listing')[0]
@@ -428,12 +428,12 @@
 
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/alsa-utils/+bug/3/+choose-affected-product
+    http://bugs.launchpad.test/alsa-utils/+bug/3/+choose-affected-product
 
     >>> user_browser.getControl('Project').value = 'alsa-utils'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/alsa-utils/+bug/3/+choose-affected-product
+    http://bugs.launchpad.test/alsa-utils/+bug/3/+choose-affected-product
 
 We get a nice error message.
 
@@ -447,20 +447,20 @@
     >>> user_browser.getControl('Continue').click()
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/evolution/+bug/3
+    http://bugs.launchpad.test/evolution/+bug/3
 
 But if we try to change it to the target of an existing upstream
 bugtask, our validator springs into action.
 
     >>> user_browser.getLink(url='evolution/+bug/3/+editstatus').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/evolution/+bug/3/+editstatus
+    http://bugs.launchpad.test/evolution/+bug/3/+editstatus
 
     >>> user_browser.getControl(name='evolution.target.product').value = (
     ...     'alsa-utils')
     >>> user_browser.getControl('Save Changes').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/evolution/+bug/3/+editstatus
+    http://bugs.launchpad.test/evolution/+bug/3/+editstatus
 
     >>> print_feedback_messages(user_browser.contents)
     There is 1 error in the data you entered...
@@ -478,12 +478,12 @@
 the URL of the remote bug.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/4/'
+    ...     'http://bugs.launchpad.test/firefox/+bug/4/'
     ...     '+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> user_browser.getControl('I have the URL').selected = True
     >>> user_browser.getControl(name='field.bug_url').value = (
@@ -494,13 +494,13 @@
 
     >>> cancel_link = user_browser.getLink('Cancel')
     >>> print(cancel_link.url)
-    http://bugs.launchpad.dev/firefox/+bug/4
+    http://bugs.launchpad.test/firefox/+bug/4
 
 But we're happy, so we add the bug watch.
 
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/gnome-terminal/+bug/4
+    http://bugs.launchpad.test/gnome-terminal/+bug/4
 
     >>> bug_watches = find_portlet(
     ...     user_browser.contents, 'Remote bug watches')
@@ -512,19 +512,19 @@
 URL is HTTP.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/4/'
+    ...     'http://bugs.launchpad.test/firefox/+bug/4/'
     ...     '+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'netapplet'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> user_browser.getControl('I have the URL').selected = True
     >>> user_browser.getControl(name='field.bug_url').value = (
     ...     u'https://bugzilla.gnome.org/bugs/show_bug.cgi?id=84')
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/netapplet/+bug/4
+    http://bugs.launchpad.test/netapplet/+bug/4
 
 The URL was automatically converted to HTTP:
 
@@ -539,19 +539,19 @@
 tracker type it is), an error message is displayed.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/4/'
+    ...     'http://bugs.launchpad.test/firefox/+bug/4/'
     ...     '+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'alsa-utils'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> user_browser.getControl('I have the URL').selected = True
     >>> user_browser.getControl(name='field.bug_url').value = (
     ...     u'http://bugs.unknown/42')
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...     print(message.renderContents())
@@ -566,7 +566,7 @@
     ...     u"http://new.trac/ticket/42";)
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> print_feedback_messages(user_browser.contents)
     ...
@@ -577,7 +577,7 @@
 
     >>> cancel_link = user_browser.getLink('Cancel')
     >>> print(cancel_link.url)
-    http://bugs.launchpad.dev/firefox/+bug/4
+    http://bugs.launchpad.test/firefox/+bug/4
 
 Now the user confirms they want us to register the bug tracker for them
 and we do that before creating the new bug watch.
@@ -587,7 +587,7 @@
 The bug watch is linked, and we're redirected to the bug's page.
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/alsa-utils/+bug/4
+    http://bugs.launchpad.test/alsa-utils/+bug/4
 
 The bug tracker and bug watch were added. We can see that the bugtracker
 has a special name, starting with 'auto-', to indicate that it was
@@ -605,19 +605,19 @@
 it to HTTP on their behalf:
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/4/'
+    ...     'http://bugs.launchpad.test/firefox/+bug/4/'
     ...     '+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'thunderbird'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
+    http://bugs.launchpad.test/firefox/+bug/4/+choose-affected-product
 
     >>> user_browser.getControl('I have the URL').selected = True
     >>> user_browser.getControl(name='field.bug_url').value = (
     ...     u'bugzilla.gnome.org/bugs/show_bug.cgi?id=168')
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/thunderbird/+bug/4
+    http://bugs.launchpad.test/thunderbird/+bug/4
 
     >>> bug_watches = find_portlet(
     ...     user_browser.contents, 'Remote bug watches')
@@ -635,7 +635,7 @@
 Similar things happen when the upstream link is an email address:
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/12/'
+    ...     'http://bugs.launchpad.test/jokosher/+bug/12/'
     ...     '+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
@@ -660,7 +660,7 @@
 
     >>> user_browser.getControl('Add to Bug Report').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/gnome-terminal/+bug/12
+    http://bugs.launchpad.test/gnome-terminal/+bug/12
 
     >>> def print_remote_bug_watches_portlet(browser):
     ...     bug_watches = find_portlet(

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-bugtracker-information.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-bugtracker-information.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-bugtracker-information.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 task.
 
     >>> user_browser.open(
-    ...    'http://launchpad.dev/firefox/+bug/1/+choose-affected-product')
+    ...    'http://launchpad.test/firefox/+bug/1/+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.contents.decode('ascii', 'ignore'))
@@ -27,7 +27,7 @@
 its bugs and prompt for a URL or an email address.
 
     >>> user_browser.open(
-    ...    'http://launchpad.dev/firefox/+bug/1/+choose-affected-product')
+    ...    'http://launchpad.test/firefox/+bug/1/+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'thunderbird'
     >>> user_browser.getControl('Continue').click()
     >>> print(user_browser.contents.decode('ascii', 'ignore'))
@@ -45,7 +45,7 @@
 appear.
 
     >>> user_browser.open(
-    ...    'http://launchpad.dev/firefox/+bug/1/+choose-affected-product')
+    ...    'http://launchpad.test/firefox/+bug/1/+choose-affected-product')
     >>> user_browser.getControl('Project').value = 'evolution'
     >>> user_browser.getControl('Continue').click()
 

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 
     >>> debian_bug = 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=42'
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/'
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/'
     ...     '+bug/8/')
     >>> user_browser.getLink(url='+distrotask').click()
     >>> user_browser.getControl('Distribution').value = ['debian']
@@ -54,7 +54,7 @@
 same bug watch.
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/'
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/'
     ...     '+bug/8/')
 
     >>> from lp.bugs.tests.bug import print_bug_affects_table
@@ -70,7 +70,7 @@
 notification is added linking to the bug. This is useful for detecting
 duplicates.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/5')
+    >>> user_browser.open('http://launchpad.test/bugs/5')
     >>> user_browser.getLink(url='+distrotask').click()
     >>> user_browser.getControl('Distribution').value = ['debian']
     >>> user_browser.getControl('Source Package Name').value = (

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-request-distribution-no-release-fix.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-request-distribution-no-release-fix.txt	2014-04-04 15:56:01 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-request-distribution-no-release-fix.txt	2019-05-22 15:20:07 +0000
@@ -8,19 +8,19 @@
 
 Gentoo is currently using Launchpad.
 
-    >>> admin_browser.open('http://launchpad.dev/gentoo/+edit')
+    >>> admin_browser.open('http://launchpad.test/gentoo/+edit')
     >>> admin_browser.getControl(
     ...     'Bugs in this project are tracked in Launchpad').selected
     True
 
 Any user can request a fix for it.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/4')
+    >>> user_browser.open('http://launchpad.test/bugs/4')
     >>> user_browser.getLink('Also affects distribution/package').click()
     >>> user_browser.getControl('Distribution').value = ['gentoo']
     >>> user_browser.getControl('Continue').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/gentoo/+bug/4'
+    'http://bugs.launchpad.test/gentoo/+bug/4'
 
 == A distribution not using Launchpad ==
 
@@ -30,12 +30,12 @@
     >>> admin_browser.getControl(
     ...     'Bugs in this project are tracked in Launchpad').selected = False
     >>> admin_browser.getControl('Change', index=3).click()
-    >>> admin_browser.open('http://launchpad.dev/gentoo/+edit')
+    >>> admin_browser.open('http://launchpad.test/gentoo/+edit')
     >>> admin_browser.getControl(
     ...     'Bugs in this project are tracked in Launchpad').selected
     False
 
-    >>> user_browser.open('http://launchpad.dev/bugs/7')
+    >>> user_browser.open('http://launchpad.test/bugs/7')
     >>> user_browser.getLink('Also affects distribution/package').click()
     >>> user_browser.getControl('Distribution').value = ['gentoo']
     >>> user_browser.getControl('Source Package').value = ''
@@ -43,4 +43,4 @@
     ...     'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1234')
     >>> user_browser.getControl('Continue').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/gentoo/+bug/7'
+    'http://bugs.launchpad.test/gentoo/+bug/7'

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-upstream-bugtracker-links.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-upstream-bugtracker-links.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-upstream-bugtracker-links.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 on the +choose-affected-product form to that bug tracker's bug filing
 and search forms.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/13/')
+    >>> user_browser.open('http://launchpad.test/bugs/13/')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> user_browser.getControl('Project').value = 'thunderbird'
     >>> user_browser.getControl('Continue').click()
@@ -29,14 +29,14 @@
 to reflect this.
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/thunderbird/+configure-bugtracker')
+    ...     'http://launchpad.test/thunderbird/+configure-bugtracker')
     >>> admin_browser.getControl(
     ...     name='field.bugtracker').value = ['external']
     >>> admin_browser.getControl(
     ...     name='field.bugtracker.bugtracker').value = 'mozilla.org'
     >>> admin_browser.getControl('Change').click()
 
-    >>> user_browser.open('http://launchpad.dev/bugs/13/')
+    >>> user_browser.open('http://launchpad.test/bugs/13/')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> user_browser.getControl('Project').value = 'thunderbird'
     >>> user_browser.getControl('Continue').click()
@@ -54,7 +54,7 @@
 set, links to the upstream bug tracker's bug filing and search forms
 will be displayed.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/13/')
+    >>> user_browser.open('http://launchpad.test/bugs/13/')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> user_browser.getControl('Project').value = 'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
@@ -82,7 +82,7 @@
 
     >>> print(long_desc)
     Originally reported at:
-      http://bugs.launchpad.dev/bugs/13
+      http://bugs.launchpad.test/bugs/13
     <BLANKLINE>
     The messages placed on this bug are for eyeball viewing of JS and
     CSS behaviour.
@@ -91,14 +91,14 @@
 filing link, such as Debbugs, only a search link will be displayed.
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/gnome-terminal/+configure-bugtracker')
+    ...     'http://launchpad.test/gnome-terminal/+configure-bugtracker')
     >>> admin_browser.getControl(
     ...     'In a registered bug tracker:').selected = True
     >>> admin_browser.getControl(
     ...     name='field.bugtracker.bugtracker').value = 'debbugs'
     >>> admin_browser.getControl('Change').click()
 
-    >>> user_browser.open('http://launchpad.dev/bugs/13/')
+    >>> user_browser.open('http://launchpad.test/bugs/13/')
     >>> user_browser.getLink(url='+choose-affected-product').click()
     >>> user_browser.getControl('Project').value = 'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
@@ -120,12 +120,12 @@
 tracker, can be set from the +configure-bugtracker page, too.
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/thunderbird/+configure-bugtracker')
+    ...     'http://launchpad.test/thunderbird/+configure-bugtracker')
     >>> admin_browser.getControl(name='field.remote_product').value = (
     ...     'Thunderbird')
     >>> admin_browser.getControl('Change').click()
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/thunderbird/+configure-bugtracker')
+    ...     'http://launchpad.test/thunderbird/+configure-bugtracker')
     >>> print(admin_browser.getControl(name='field.remote_product').value)
     Thunderbird

=== modified file 'lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt'
--- lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt	2019-05-22 15:20:07 +0000
@@ -2,7 +2,7 @@
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open(
-    ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
+    ...     "http://bugs.launchpad.test/debian/+source/mozilla-firefox/";
     ...     "+bug/2/+secrecy")
 
 Foo Bar is not Cc'd on this bug, but is able to set the bug private
@@ -11,12 +11,12 @@
     >>> browser.getControl("Private", index=1).selected = True
     >>> browser.getControl("Change").click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/2
+    http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/2
 
 When we go back to the secrecy form, the previously set value is pre-selected.
 
     >>> browser.open(
-    ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
+    ...     "http://bugs.launchpad.test/debian/+source/mozilla-firefox/";
     ...     "+bug/2/+secrecy")
     >>> browser.getControl("Private", index=1).selected
     True
@@ -25,7 +25,7 @@
 bug page.
 
     >>> browser = setupBrowser("Basic foo.bar@xxxxxxxxxxxxx:test")
-    >>> browser.open("http://launchpad.dev/ubuntu/+filebug";)
+    >>> browser.open("http://launchpad.test/ubuntu/+filebug";)
 
 The Ubuntu maintainer, Ubuntu Team, will be subscribed.
 
@@ -41,7 +41,7 @@
 
     >>> bug_id = browser.url.split("/")[-1]
     >>> print(browser.url.replace(bug_id, "BUG-ID"))
-    http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/BUG-ID
+    http://bugs.launchpad.test/ubuntu/+source/evolution/+bug/BUG-ID
 
     >>> print(browser.contents)
     <!DOCTYPE...
@@ -49,7 +49,7 @@
 
 Foo Bar sees the private bug they filed.
 
-    >>> browser.open("http://launchpad.dev/ubuntu/+bugs";)
+    >>> browser.open("http://launchpad.test/ubuntu/+bugs";)
     >>> print(browser.contents.replace(bug_id, "BUG-ID"))
     <!DOCTYPE...
     ...
@@ -76,14 +76,14 @@
 
 Not directly.
 
-    >>> anon_browser.open("http://launchpad.dev/bugs/14";)
+    >>> anon_browser.open("http://launchpad.test/bugs/14";)
     Traceback (most recent call last):
       ...
     NotFound: ...
 
 And not in bug listings.
 
-    >>> anon_browser.open("http://launchpad.dev/ubuntu/+bugs";)
+    >>> anon_browser.open("http://launchpad.test/ubuntu/+bugs";)
     >>> "a private bug" not in anon_browser.contents
     True
 
@@ -92,14 +92,14 @@
 Neither directly.
 
     >>> browser = setupBrowser("Basic no-privs@xxxxxxxxxxxxx:test")
-    >>> browser.open("http://launchpad.dev/bugs/14";)
+    >>> browser.open("http://launchpad.test/bugs/14";)
     Traceback (most recent call last):
       ...
     NotFound: ...
 
 Nor in a search listing.
 
-    >>> browser.open("http://launchpad.dev/ubuntu/+bugs";)
+    >>> browser.open("http://launchpad.test/ubuntu/+bugs";)
     >>> "a private bug" not in browser.contents
     True
 
@@ -132,10 +132,10 @@
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> add_subscriber_url = (
-    ...     "http://launchpad.dev/ubuntu/+source/evolution/+bug/%s";
+    ...     "http://launchpad.test/ubuntu/+source/evolution/+bug/%s";
     ...     "/+addsubscriber" % latest_evo_bug)
     >>> browser.open(add_subscriber_url)
     >>> browser.getControl("Person").value = "name12"
     >>> browser.getControl("Subscribe user").click()
     >>> browser.url
-    'http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/...'
+    'http://bugs.launchpad.test/ubuntu/+source/evolution/+bug/...'

=== modified file 'lib/lp/bugs/stories/bug-privacy/xx-presenting-private-bugs.txt'
--- lib/lp/bugs/stories/bug-privacy/xx-presenting-private-bugs.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-privacy/xx-presenting-private-bugs.txt	2019-05-22 15:20:07 +0000
@@ -4,25 +4,25 @@
 When a bug report is public, it says so.
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
-    >>> browser.open("http://launchpad.dev/bugs/4";)
+    >>> browser.open("http://launchpad.test/bugs/4";)
     >>> print(extract_text(find_tag_by_id(browser.contents, 'privacy')))
     This report contains Public information...
 
 But when marked private, it gains the standard Launchpad presentation
 for private things.
 
-    >>> browser.open("http://bugs.launchpad.dev/firefox/+bug/4/+secrecy";)
+    >>> browser.open("http://bugs.launchpad.test/firefox/+bug/4/+secrecy";)
     >>> browser.getControl("Private", index=1).selected = True
     >>> browser.getControl("Change").click()
     >>> print(browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/4
+    http://bugs.launchpad.test/firefox/+bug/4
     >>> print(extract_text(find_tag_by_id(browser.contents, 'privacy')))
     This report contains Private information...
 
 Bugs created before we started recording the date and time and who
 marked the bug private show only a simple message:
 
-    >>> browser.open("http://launchpad.dev/bugs/14";)
+    >>> browser.open("http://launchpad.test/bugs/14";)
     >>> print(extract_text(find_tag_by_id(browser.contents, 'privacy')))
     This report contains Private Security information...
 
@@ -30,7 +30,7 @@
 bugs or where the product requests that bugs are private by default)
 have the full message:
 
-    >>> browser.open("http://bugs.launchpad.dev/firefox/+filebug";)
+    >>> browser.open("http://bugs.launchpad.test/firefox/+filebug";)
     >>> browser.getControl('Summary', index=0).value = (
     ...     'Firefox crashes when I change the default route')
     >>> browser.getControl('Continue').click()
@@ -40,7 +40,7 @@
     >>> browser.getControl("Submit Bug Report").click()
 
     >>> print(browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/...
+    http://bugs.launchpad.test/firefox/+bug/...
     >>> print(extract_text(find_tag_by_id(browser.contents, 'privacy')))
     This report contains Private Security information...
 
@@ -53,19 +53,19 @@
 
 Of course, Foo Bar gets redirected:
 
-    >>> browser.open('http://bugs.launchpad.dev/bugs/4')
+    >>> browser.open('http://bugs.launchpad.test/bugs/4')
     >>> browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/4'
+    'http://bugs.launchpad.test/firefox/+bug/4'
 
 But poor old no privs does not, and neither do anonymous users:
 
     >>> browser = setupBrowser(auth="Basic no-priv@xxxxxxxxxxxxx:test")
-    >>> browser.open('http://bugs.launchpad.dev/bugs/4')
+    >>> browser.open('http://bugs.launchpad.test/bugs/4')
     Traceback (most recent call last):
     ...
     NotFound: ...
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/bugs/4')
+    >>> anon_browser.open('http://bugs.launchpad.test/bugs/4')
     Traceback (most recent call last):
     ...
     NotFound: ...

=== modified file 'lib/lp/bugs/stories/bug-release-management/nomination-navigation.txt'
--- lib/lp/bugs/stories/bug-release-management/nomination-navigation.txt	2015-06-26 14:00:41 +0000
+++ lib/lp/bugs/stories/bug-release-management/nomination-navigation.txt	2019-05-22 15:20:07 +0000
@@ -8,15 +8,15 @@
 same navigation as the bug page itself.
 
     >>> admin_browser.open(
-    ... 'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1'
+    ... 'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1'
     ... '/nominations/2/+editstatus')
     >>> print_location(admin_browser.contents)
     Hierarchy: Ubuntu > mozilla-firefox package > Bug #1...
     Tabs:
-    * Overview - http://launchpad.dev/ubuntu/+source/mozilla-firefox
-    * Code - http://code.launchpad.dev/ubuntu/+source/mozilla-firefox
-    * Bugs (selected) - http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox
+    * Overview - http://launchpad.test/ubuntu/+source/mozilla-firefox
+    * Code - http://code.launchpad.test/ubuntu/+source/mozilla-firefox
+    * Bugs (selected) - http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox
     * Blueprints - not linked
-    * Translations - http://translations.launchpad.dev/ubuntu/+source/mozilla-firefox
-    * Answers - http://answers.launchpad.dev/ubuntu/+source/mozilla-firefox
+    * Translations - http://translations.launchpad.test/ubuntu/+source/mozilla-firefox
+    * Answers - http://answers.launchpad.test/ubuntu/+source/mozilla-firefox
     Main heading: Approve or decline nomination for bug #1 in Ubuntu Hoary

=== modified file 'lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt'
--- lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt	2010-12-21 19:11:49 +0000
+++ lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 Anonymous users should not be able to nominate bugs for release because
 launchpad.Edit permission is required to do so.:
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/jokosher/+bug/12')
+    >>> anon_browser.open('http://bugs.launchpad.test/jokosher/+bug/12')
 
     >>> anon_browser.getLink('Nominate for series').click()
     Traceback (most recent call last):

=== modified file 'lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt'
--- lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 
     >>> no_priv_browser = setupBrowser(
     ...     auth="Basic no-priv@xxxxxxxxxxxxx:test")
-    >>> no_priv_browser.open("http://launchpad.dev/bugs/1";)
+    >>> no_priv_browser.open("http://launchpad.test/bugs/1";)
 
     >>> no_priv_browser.getControl("Approve")
     Traceback (most recent call last):
@@ -19,7 +19,7 @@
 
 But an admin can see them.
 
-    >>> admin_browser.open("http://bugs.launchpad.dev/bugs/1";)
+    >>> admin_browser.open("http://bugs.launchpad.test/bugs/1";)
     >>> approve_button = admin_browser.getControl("Approve", index=0)
     >>> decline_button = admin_browser.getControl("Decline", index=0)
 
@@ -34,21 +34,21 @@
 
 After a productseries task has been created, it's editable.
 
-    >>> user_browser.open('http://launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink(url='firefox/1.0/+bug/1/+editstatus').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/1.0/+bug/1/+editstatus'
+    'http://bugs.launchpad.test/firefox/1.0/+bug/1/+editstatus'
 
     >>> user_browser.getControl('Status').value
     ['New']
     >>> user_browser.getControl('Status').value = ['Confirmed']
     >>> user_browser.getControl('Save Changes').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/1.0/+bug/1'
+    'http://bugs.launchpad.test/firefox/1.0/+bug/1'
 
 Privileged users can decline bug nominations.
 
-    >>> admin_browser.open("http://bugs.launchpad.dev/bugs/1";)
+    >>> admin_browser.open("http://bugs.launchpad.test/bugs/1";)
     >>> approve_button = admin_browser.getControl("Approve", index=0)
     >>> decline_button = admin_browser.getControl("Decline", index=0)
 
@@ -84,7 +84,7 @@
     ...     auth='Basic %s:test' % nominater.preferredemail.email)
     >>> logout()
     >>> nominater_browser.open(
-    ...     "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate"; %
+    ...     "http://launchpad.test/poseidon/+source/%s/+bug/%s/+nominate"; %
     ...     (dsp.name, bug_task.bug.id))
 
 Before we continue, we'll set up a second browser instance, to simulate
@@ -97,7 +97,7 @@
     ...     auth='Basic %s:test' % nominater.preferredemail.email)
     >>> logout()
     >>> nominater_other_browser.open(
-    ...     "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate"; %
+    ...     "http://launchpad.test/poseidon/+source/%s/+bug/%s/+nominate"; %
     ...     (dsp.name, bug_task.bug.id))
     >>> nominater_browser.getControl("Aqua").selected = True
     >>> nominater_browser.getControl("Nominate").click()
@@ -122,7 +122,7 @@
 approved and targeted to the release.
 
     >>> admin_browser.open(
-    ...     "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate"; %
+    ...     "http://launchpad.test/poseidon/+source/%s/+bug/%s/+nominate"; %
     ...     (dsp.name, bug_task.bug.id))
 
     >>> admin_browser.getControl("Hydro").selected = True
@@ -149,7 +149,7 @@
     ...     auth='Basic %s:test' % nominater.preferredemail.email)
     >>> logout()
     >>> nominater_browser.open(
-    ...     "http://launchpad.dev/widget/+bug/%s/+nominate"; % bug.id)
+    ...     "http://launchpad.test/widget/+bug/%s/+nominate"; % bug.id)
 
 Before we continue, we'll set up a second browser instance, to simulate
 the nominater accessing the site from another window. Working with the same
@@ -161,7 +161,7 @@
     ...     auth='Basic %s:test' % nominater.preferredemail.email)
     >>> logout()
     >>> nominater_other_browser.open(
-    ...     "http://launchpad.dev/widget/+bug/%s/+nominate"; % bug.id)
+    ...     "http://launchpad.test/widget/+bug/%s/+nominate"; % bug.id)
 
     >>> nominater_browser.getControl("Beta").selected = True
     >>> nominater_other_browser.getControl("Beta").selected = True
@@ -186,7 +186,7 @@
 approved and targeted to the release.
 
     >>> admin_browser.open(
-    ...     "http://launchpad.dev/widget/+bug/%s/+nominate"; % bug.id)
+    ...     "http://launchpad.test/widget/+bug/%s/+nominate"; % bug.id)
 
     >>> admin_browser.getControl("Trunk").selected = True
     >>> admin_browser.getControl("Target").click()
@@ -199,16 +199,16 @@
 distribution task is no longer editable. Instead the status is tracked
 in the release task.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/ubuntu/+bug/2')
+    >>> user_browser.open('http://bugs.launchpad.test/ubuntu/+bug/2')
     >>> ubuntu_edit_url = (
-    ...     'http://bugs.launchpad.dev/ubuntu/+bug/2/+editstatus')
+    ...     'http://bugs.launchpad.test/ubuntu/+bug/2/+editstatus')
     >>> user_browser.getLink(url=ubuntu_edit_url)
     Traceback (most recent call last):
     ...
     LinkNotFoundError...
 
     >>> ubuntu_hoary_edit_url = (
-    ...     'http://bugs.launchpad.dev/ubuntu/hoary/+bug/2/+editstatus')
+    ...     'http://bugs.launchpad.test/ubuntu/hoary/+bug/2/+editstatus')
     >>> user_browser.getLink(url=ubuntu_hoary_edit_url) is not None
     True
 
@@ -216,7 +216,7 @@
 illustrate conjoined bugtasks, so we'll make 'no-priv' the bug supervisor
 for Ubuntu:
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/ubuntu/+bugsupervisor')
+    >>> admin_browser.open('http://bugs.launchpad.test/ubuntu/+bugsupervisor')
     >>> admin_browser.getControl('Bug Supervisor').value = 'no-priv'
     >>> admin_browser.getControl('Change').click()
 
@@ -282,20 +282,20 @@
 in the series task.
 
     >>> admin_browser.open(
-    ...     "http://launchpad.dev/products/firefox/+bug/4/+nominate";)
+    ...     "http://launchpad.test/products/firefox/+bug/4/+nominate";)
     >>> admin_browser.getControl("Trunk").selected = True
     >>> admin_browser.getControl("Target").click()
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/4')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/4')
     >>> firefox_edit_url = (
-    ...     'http://bugs.launchpad.dev/firefox/+bug/4/+editstatus')
+    ...     'http://bugs.launchpad.test/firefox/+bug/4/+editstatus')
     >>> user_browser.getLink(url=firefox_edit_url)
     Traceback (most recent call last):
     ...
     LinkNotFoundError...
 
     >>> firefox_trunk_edit_url = (
-    ...     'http://bugs.launchpad.dev/firefox/trunk/+bug/4/+editstatus')
+    ...     'http://bugs.launchpad.test/firefox/trunk/+bug/4/+editstatus')
     >>> user_browser.getLink(url=firefox_trunk_edit_url) is not None
     True
 
@@ -303,7 +303,7 @@
 illustrate conjoined bugtasks, so we'll make 'no-priv' the bug supervisor
 for Firefox:
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/firefox/+bugsupervisor')
+    >>> admin_browser.open('http://bugs.launchpad.test/firefox/+bugsupervisor')
     >>> admin_browser.getControl('Bug Supervisor').value = 'no-priv'
     >>> admin_browser.getControl('Change').click()
 
@@ -322,7 +322,7 @@
     >>> user_browser.getControl('Status').displayValue = ["Won't Fix"]
     >>> user_browser.getControl('Save Changes').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/trunk/+bug/4'
+    'http://bugs.launchpad.test/firefox/trunk/+bug/4'
 
 Now both the general and series tasks are editable.
 
@@ -371,7 +371,7 @@
 targeted to it.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/firefox/1.0/+bugs')
+    ...     'http://launchpad.test/firefox/1.0/+bugs')
 
     >>> from lp.bugs.tests.bug import print_bugtasks
     >>> print_bugtasks(anon_browser.contents)

=== modified file 'lib/lp/bugs/stories/bug-tags/xx-official-bug-tags.txt'
--- lib/lp/bugs/stories/bug-tags/xx-official-bug-tags.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-tags/xx-official-bug-tags.txt	2019-05-22 15:20:07 +0000
@@ -5,10 +5,10 @@
 from the main bug page.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox')
+    ...     'http://bugs.launchpad.test/firefox')
     >>> admin_browser.getLink('Edit official tags').click()
     >>> print(admin_browser.url)
-    http://bugs.launchpad.dev/firefox/+manage-official-tags
+    http://bugs.launchpad.test/firefox/+manage-official-tags
 
 Tags are entered into a textarea as a list of white-spaces separated
 words.
@@ -16,9 +16,9 @@
     >>> admin_browser.getControl('Official Bug Tags').value = 'foo bar'
     >>> admin_browser.getControl('Save').click()
     >>> print(admin_browser.url)
-    http://bugs.launchpad.dev/firefox
+    http://bugs.launchpad.test/firefox
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+manage-official-tags')
+    ...     'http://bugs.launchpad.test/firefox/+manage-official-tags')
     >>> print(admin_browser.getControl('Official Bug Tags').value)
     bar foo
 
@@ -26,14 +26,14 @@
 distributions but not for other bug targets.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/1.0')
+    ...     'http://bugs.launchpad.test/firefox/1.0')
     >>> print(admin_browser.getLink('Edit official tags'))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/1.0/+manage-official-tags')
+    ...     'http://bugs.launchpad.test/firefox/1.0/+manage-official-tags')
     Traceback (most recent call last):
     ...
     NotFound...
@@ -41,14 +41,14 @@
 The link as well as the edit form is only available for project
 administrators but not for ordinary users.
 
-    >>> browser.open('http://bugs.launchpad.dev/firefox')
+    >>> browser.open('http://bugs.launchpad.test/firefox')
     >>> print(browser.getLink('Edit official tags'))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
     >>> browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+manage-official-tags')
+    ...     'http://bugs.launchpad.test/firefox/+manage-official-tags')
     Traceback (most recent call last):
     ...
     Unauthorized...
@@ -66,19 +66,19 @@
     ...     auth='Basic %s:test' % supervisor.preferredemail.email)
     >>> logout()
     >>> bug_super_browser.open(
-    ...     'http://bugs.launchpad.dev/youbuntu')
+    ...     'http://bugs.launchpad.test/youbuntu')
     >>> bug_super_browser.getLink('Edit official tags').click()
     >>> print(bug_super_browser.url)
-    http://bugs.launchpad.dev/youbuntu/+manage-official-tags
+    http://bugs.launchpad.test/youbuntu/+manage-official-tags
 
 The bug supervisor can also set the tags for the product.
 
     >>> bug_super_browser.getControl('Official Bug Tags').value = 'foo bar'
     >>> bug_super_browser.getControl('Save').click()
     >>> print(bug_super_browser.url)
-    http://bugs.launchpad.dev/youbuntu
+    http://bugs.launchpad.test/youbuntu
     >>> bug_super_browser.open(
-    ...     'http://bugs.launchpad.dev/youbuntu/+manage-official-tags')
+    ...     'http://bugs.launchpad.test/youbuntu/+manage-official-tags')
     >>> print(bug_super_browser.getControl('Official Bug Tags').value)
     bar foo
 

=== modified file 'lib/lp/bugs/stories/bug-tags/xx-searching-for-tags.txt'
--- lib/lp/bugs/stories/bug-tags/xx-searching-for-tags.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-tags/xx-searching-for-tags.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 On the advanced search page it's possible to search for a specific tag.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    ...     'http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> anon_browser.getControl('Tags').value = 'crash'
     >>> anon_browser.getControl('Search', index=0).click()
 
@@ -16,7 +16,7 @@
 shown.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    ...     'http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> anon_browser.getControl('Tags').value = 'crash dataloss'
     >>> anon_browser.getControl('Search', index=0).click()
     >>> print_bugtasks(anon_browser.contents)
@@ -27,7 +27,7 @@
 If an invalid tag name is entered, an error message will be displayed.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    ...     'http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> anon_browser.getControl('Tags').value = '!!invalid!!'
     >>> anon_browser.getControl('Search', index=0).click()
 
@@ -45,7 +45,7 @@
 to prevent XSS.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    ...     'http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> anon_browser.getControl('Tags').value = (
     ...     '<script>alert("cheezburger");</script>')
     >>> anon_browser.getControl('Search', index=0).click()

=== modified file 'lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-listings-page.txt'
--- lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-listings-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-listings-page.txt	2019-05-22 15:20:07 +0000
@@ -2,10 +2,10 @@
 have been used for bugs in this context. The content of this portlet is loaded
 using Javascript after page load.
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs')
     >>> anon_browser.getLink(id='tags-content-link').click()
     >>> print(anon_browser.url)
-    http://launchpad.dev/ubuntu/+bugtarget-portlet-tags-content
+    http://launchpad.test/ubuntu/+bugtarget-portlet-tags-content
     >>> tags_portlet = find_tags_by_class(anon_browser.contents, 'data-list')[0]
     >>> for a_tag in tags_portlet('a'):
     ...     print(a_tag.renderContents())
@@ -17,7 +17,7 @@
 
     >>> anon_browser.getLink(url='crash').click()
     >>> anon_browser.url
-    'http://launchpad.dev/ubuntu/+bugs?field.tag=crash'
+    'http://launchpad.test/ubuntu/+bugs?field.tag=crash'
 
     >>> from lp.bugs.tests.bug import print_bugtasks
     >>> print_bugtasks(anon_browser.contents)
@@ -31,10 +31,10 @@
 shown.
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+bugtarget-portlet-tags-content')
+    ...     'http://launchpad.test/ubuntu/+bugtarget-portlet-tags-content')
     >>> anon_browser.getLink('dataloss').click()
     >>> anon_browser.url
-    'http://launchpad.dev/ubuntu/+bugs?field.tag=dataloss'
+    'http://launchpad.test/ubuntu/+bugs?field.tag=dataloss'
     >>> print_bugtasks(anon_browser.contents)
     2 Blackhole Trash folder
       Ubuntu Medium New
@@ -42,11 +42,11 @@
 We update bug #2's status to Invalid to demonstrate that the portlet body is
 not available when no tags are relevant:
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/tomcat/+bug/2')
+    >>> admin_browser.open('http://bugs.launchpad.test/tomcat/+bug/2')
     >>> admin_browser.getControl("Status", index=0).value = ["Invalid"]
     >>> admin_browser.getControl("Save Changes", index=0).click()
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/tomcat/+bugtarget-portlet-tags-content')
+    ...     'http://launchpad.test/tomcat/+bugtarget-portlet-tags-content')
     >>> print(extract_text(anon_browser.contents))
     Tags

=== modified file 'lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-page.txt'
--- lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bug-tags/xx-tags-on-bug-page.txt	2019-05-22 15:20:07 +0000
@@ -1,8 +1,8 @@
 Users can see bugs tags on the bug page.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
     >>> print(extract_text(find_tag_by_id(user_browser.contents, 'bug-tags')))
     Add tags  Tag help
 
@@ -45,7 +45,7 @@
 Simply changing the ordering of the bug tags won't cause anything to
 happen.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1/+edit')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1/+edit')
     >>> tags = user_browser.getControl('Tags')
     >>> tags.value
     'bar foo'
@@ -53,28 +53,28 @@
     >>> tags.value = "foo bar"
     >>> user_browser.getControl('Change').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 Let's delete the tags we added.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1/+edit')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1/+edit')
     >>> user_browser.getControl('Tags').value
     'bar foo'
     >>> user_browser.getControl('Tags').value = ''
     >>> user_browser.getControl('Change').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 If we look at a bug having tags, the displayed tags are links, linking
 to the list of all bugs having that tag in this context.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+bug/2')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+bug/2')
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bug/2'
+    'http://bugs.launchpad.test/ubuntu/+bug/2'
 
     >>> anon_browser.getLink('dataloss').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bugs?field.tag=dataloss'
+    'http://bugs.launchpad.test/ubuntu/+bugs?field.tag=dataloss'
 
     >>> from lp.bugs.tests.bug import print_bugtasks
     >>> print_bugtasks(anon_browser.contents)

=== modified file 'lib/lp/bugs/stories/bugattachments/xx-attachments-to-bug-report.txt'
--- lib/lp/bugs/stories/bugattachments/xx-attachments-to-bug-report.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugattachments/xx-attachments-to-bug-report.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 Without an attachment to the bug report, the first displayed comment
 of bug #11 is:
 
-    >>> browser.open("http://bugs.launchpad.dev/redfish/+bug/11";)
+    >>> browser.open("http://bugs.launchpad.test/redfish/+bug/11";)
     >>> comment_1 = find_tags_by_class(browser.contents, 'boardComment')[0]
     >>> print(extract_text(comment_1))
     Valentina Commissari (tsukimi)
@@ -40,7 +40,7 @@
 does not display a text message (which would be the same as the text
 displayed as the bug report).
 
-    >>> browser.open("http://bugs.launchpad.dev/redfish/+bug/11";)
+    >>> browser.open("http://bugs.launchpad.test/redfish/+bug/11";)
     >>> comment_0 = find_tags_by_class(browser.contents, 'boardComment')[0]
     >>> print(extract_text(comment_0))
     Daniel Silverstone (kinnison)
@@ -52,7 +52,7 @@
     text/plain)
     >>> link = browser.getLink('sample data')
     >>> print(link.url)
-    http://bugs.launchpad.dev/jokosher/+bug/11/+attachment/.../+files/test.txt
+    http://bugs.launchpad.test/jokosher/+bug/11/+attachment/.../+files/test.txt
     >>> print(comment_0.find('a', text='Edit').parent)
     <a class="sprite edit action-icon"
        href="/jokosher/+bug/11/+attachment/...">Edit</a>
@@ -69,7 +69,7 @@
     ...     is_patch=True, content_type='text/plain',
     ...     description='a patch')
     >>> logout()
-    >>> browser.open("http://bugs.launchpad.dev/redfish/+bug/11";)
+    >>> browser.open("http://bugs.launchpad.test/redfish/+bug/11";)
     >>> print(extract_text(browser.contents))
     Bug #11 ...
     ...Patches...

=== modified file 'lib/lp/bugs/stories/bugattachments/xx-bugattachments.txt'
--- lib/lp/bugs/stories/bugattachments/xx-bugattachments.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugattachments/xx-bugattachments.txt	2019-05-22 15:20:07 +0000
@@ -1,7 +1,7 @@
 We need to login in order to add attachments.
 
   >>> anon_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   Traceback (most recent call last):
   ...
   Unauthorized:...
@@ -9,7 +9,7 @@
 When we're logged in we can access the page.
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
 
 Let's add an attachment. First create a file-like object.
 
@@ -30,7 +30,7 @@
 After we added the attachment, we get redirected to the bug page.
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
 We can check that the attachment is there
 
@@ -41,7 +41,7 @@
 
   >>> link = user_browser.getLink('Some information')
   >>> link.url
-  'http://bugs.launchpad.dev/firefox/+bug/1/+attachment/.../+files/foo.txt'
+  'http://bugs.launchpad.test/firefox/+bug/1/+attachment/.../+files/foo.txt'
 
   >>> b'Added some information' in user_browser.contents
   True
@@ -57,7 +57,7 @@
 also not necessary to enter a comment in order to add an attachment.
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> bar_file = StringIO('Traceback...')
   >>> user_browser.getControl('Attachment').add_file(
   ...   bar_file, 'text/plain', 'bar.txt')
@@ -65,7 +65,7 @@
   >>> user_browser.getControl(name="field.comment").value = ''
   >>> user_browser.getControl('Post Comment').click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
   >>> attachments = find_portlet(user_browser.contents, 'Bug attachments')
   >>> for li_tag in attachments.findAll('li', 'download-attachment'):
@@ -76,7 +76,7 @@
 We can also declare an attachment to be a patch.
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
 
 Leading and trailing whitespace are stripped from the description of the
 attachment.
@@ -92,13 +92,13 @@
   ...     name="field.comment").value = 'Added some information'
   >>> user_browser.getControl('Post Comment').click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
 If we add an attachment that looks like a patch but if we don't set
 the flag "this attachment is a patch"...
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> foo_file.seek(0)
   >>> user_browser.getControl('Attachment').add_file(
   ...   foo_file, 'text/plain', 'foo2.diff')
@@ -113,7 +113,7 @@
 ...we are redirected to a page...
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1/+attachment/.../+confirm-is-patch'
+  'http://bugs.launchpad.test/firefox/+bug/1/+attachment/.../+confirm-is-patch'
 
 ...where we see a message that we should double-check if this file
 is indeed not a patch.
@@ -143,7 +143,7 @@
 listed as an ordinary attachment.
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
   >>> attachments = find_portlet(user_browser.contents, 'Bug attachments')
   >>> for li_tag in attachments.findAll('li', 'download-attachment'):
   ...   print(li_tag.a.renderContents())
@@ -155,7 +155,7 @@
 if we set the "patch" flag for this attachment...
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> foo_file.seek(0)
   >>> user_browser.getControl('Attachment').add_file(
   ...   foo_file, 'text/plain', 'foo.png')
@@ -169,7 +169,7 @@
 is indeed a patch.
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1/+attachment/.../+confirm-is-patch'
+  'http://bugs.launchpad.test/firefox/+bug/1/+attachment/.../+confirm-is-patch'
 
 ...where we see a message asking us if we really ant to declare this file
 as a patch.
@@ -197,7 +197,7 @@
 Now we are redirected to the main bug page...
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
 ...and the new attachment is listed as a patch.
 
@@ -212,7 +212,7 @@
 ".debdiff", or ".patch" are patch attachments:
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> foo_file.seek(0)
   >>> user_browser.getControl('Attachment').add_file(
   ...   foo_file, 'text/plain', 'foo3.diff')
@@ -224,10 +224,10 @@
   ...     name="field.comment").value = 'Add foo3.diff as a patch.'
   >>> user_browser.getControl('Post Comment').click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> foo_file.seek(0)
   >>> user_browser.getControl('Attachment').add_file(
   ...   foo_file, 'text/plain', 'foo4.debdiff')
@@ -239,10 +239,10 @@
   ...     name="field.comment").value = 'Add foo4.debdiff as a patch.'
   >>> user_browser.getControl('Post Comment').click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
   >>> user_browser.open(
-  ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addcomment')
+  ...     'http://bugs.launchpad.test/firefox/+bug/1/+addcomment')
   >>> foo_file.seek(0)
   >>> user_browser.getControl('Attachment').add_file(
   ...   foo_file, 'text/plain', 'foo5.patch')
@@ -254,15 +254,15 @@
   ...     name="field.comment").value = 'Add foo5.patch as a patch.'
   >>> user_browser.getControl('Post Comment').click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/firefox/+bug/1'
+  'http://bugs.launchpad.test/firefox/+bug/1'
 
 We can also edit the attachment details, let's navigate to that page.
 
     >>> import re
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink(url=re.compile(r'[+]attachment/\d+$')).click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1/+attachment/...'
+    'http://bugs.launchpad.test/firefox/+bug/1/+attachment/...'
 
     >>> b'Edit attachment' in user_browser.contents
     True
@@ -271,7 +271,7 @@
 page, maintaining the firefox context.
 
     >>> user_browser.getLink('Cancel')
-    <Link text='Cancel' url='http://bugs.launchpad.dev/firefox/+bug/1'>
+    <Link text='Cancel' url='http://bugs.launchpad.test/firefox/+bug/1'>
 
 After editing the attachment details (we leave some leading and trailing
 whitespace to test that's correctly stripped)...
@@ -283,7 +283,7 @@
 ...we're redirected to the bug page
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
     >>> b'Another title' in user_browser.contents
     True
@@ -301,7 +301,7 @@
 the attachment should indeed be labeled as a patch
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1/+attachment/.../+confirm-is-patch'
+    'http://bugs.launchpad.test/firefox/+bug/1/+attachment/.../+confirm-is-patch'
 
     >>> print(extract_text(find_tags_by_class(
     ...     user_browser.contents, 'documentDescription')[0]))
@@ -324,7 +324,7 @@
 Now we are redirected to the main bug page...
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 ...the attachment that became a patch is now shown in the portlet
 "Patches"...
@@ -351,7 +351,7 @@
 to add bug comments with the checkbox "This attachment is a patch"
 enabled.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink('Add patch').click()
     >>> patch_checkbox = user_browser.getControl(
     ...     'This attachment contains a solution (patch) for this bug')
@@ -363,7 +363,7 @@
     >>> from StringIO import StringIO
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment";)
 
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO("Traceback..."), "text/plain", "foo.txt")
@@ -375,7 +375,7 @@
 And a patch...
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox";
+    ...     "http://bugs.launchpad.test/debian/+source/mozilla-firefox";
     ...     "/+bug/2/+addcomment")
 
     >>> user_browser.getControl("Attachment").add_file(
@@ -389,7 +389,7 @@
 And another patch...
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/4/+addcomment";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/4/+addcomment";)
 
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO("Patch..."), "text/plain", "foo.patch")
@@ -401,7 +401,7 @@
 
 And now we'll search for patches for firefox bugs.
 
-    >>> user_browser.open("http://bugs.launchpad.dev/firefox/+bugs?advanced=1";)
+    >>> user_browser.open("http://bugs.launchpad.test/firefox/+bugs?advanced=1";)
     >>> user_browser.getControl(
     ...     "Show only bugs with patches available").selected = True
     >>> user_browser.getControl("Search", index=1).click()

=== modified file 'lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt'
--- lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 deleted again from the bug attachment edit page.
 
     >>> from cStringIO import StringIO
-    >>> user_browser.open('http://launchpad.dev/bugs/2')
+    >>> user_browser.open('http://launchpad.test/bugs/2')
     >>> user_browser.open(user_browser.url + '/+addcomment')
     >>> foo_file = StringIO('V1agra.')
     >>> user_browser.getControl('Attachment').add_file(
@@ -14,7 +14,7 @@
 
 The attachment is now visible on the bug page.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/2')
+    >>> user_browser.open('http://launchpad.test/bugs/2')
     >>> attachment_portlet = find_portlet(
     ...     user_browser.contents, 'Bug attachments')
     >>> for li in attachment_portlet.findAll('li', 'download-attachment'):
@@ -52,7 +52,7 @@
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
     ...     print(message.renderContents())
     Attachment
-    "<a href="http://bugs.launchpad.dev/...+files/foo.txt";>Great deal</a>"
+    "<a href="http://bugs.launchpad.test/...+files/foo.txt";>Great deal</a>"
     has been deleted.
 
     >>> print(find_portlet(user_browser.contents, 'Bug attachments'))

=== modified file 'lib/lp/bugs/stories/bugattachments/xx-display-filesize-attachment.txt'
--- lib/lp/bugs/stories/bugattachments/xx-display-filesize-attachment.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugattachments/xx-display-filesize-attachment.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
     >>> foo_file = StringIO(filecontent)
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment";)
 
     >>> user_browser.getControl("Attachment").add_file(
     ...     foo_file, "text/plain", "foo.txt")
@@ -15,7 +15,7 @@
     >>> user_browser.getControl(name="field.comment").value = "comment comment"
     >>> user_browser.getControl("Post Comment").click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
     >>> last_comment = find_tags_by_class(
     ...     user_browser.contents, 'boardCommentBody')[-1]
@@ -38,7 +38,7 @@
     >>> foo_file = StringIO(filecontent)
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment";)
 
     >>> user_browser.getControl("Attachment").add_file(
     ...     foo_file, "text/plain", "foo.txt")
@@ -46,7 +46,7 @@
     >>> user_browser.getControl(name="field.comment").value = "comment comment"
     >>> user_browser.getControl("Post Comment").click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
     >>> last_comment = find_tags_by_class(
     ...     user_browser.contents, 'boardCommentBody')[-1]

=== modified file 'lib/lp/bugs/stories/bugs/bug-add-subscriber.txt'
--- lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 
 Anonymous users should not be able to subscribe someone else to a bug.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> anon_browser.getLink('Subscribe someone else').click()
     Traceback (most recent call last):
     ...
@@ -16,17 +16,17 @@
 No Privileges wants to subscribe David Allouche to the bug because they know
 that he's interested in that feature.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink('Subscribe someone else').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber'
+    'http://bugs.launchpad.test/firefox/+bug/1/+addsubscriber'
 
 If No Privileges has a sudden change of heart, there's a cancel link
 that will return the browser to the bug page.
 
     >>> cancel_link = user_browser.getLink('Cancel')
     >>> cancel_link.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 By looking at the 'Subscribers' portlet, they see that David Allouche is not
 currently subscribed to the bug:
@@ -35,7 +35,7 @@
     ...     print_also_notified, print_direct_subscribers)
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(user_browser.contents)
     Sample Person
@@ -47,11 +47,11 @@
 
 They subscribe David Allouche to the bug using his Launchpad username.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1/+addsubscriber')
     >>> user_browser.getControl("Person").value = 'ddaa'
     >>> user_browser.getControl("Subscribe user").click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 They are notified that David Allouche has been subscribed.
 
@@ -63,7 +63,7 @@
 The subscribers portlet now contains the new subscriber's name.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(user_browser.contents)
     David Allouche (Unsubscribe)
@@ -86,16 +86,16 @@
     ...
     You have been subscribed to a public bug by No Privileges Person (no-priv):
     ...
-    http://bugs.launchpad.dev/bugs/...
+    http://bugs.launchpad.test/bugs/...
 
 He subscribes the Landscape developers team.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink('Subscribe someone else').click()
     >>> user_browser.getControl("Person").value = 'landscape-developers'
     >>> user_browser.getControl("Subscribe user").click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
 He is notified that Landscape developers team has been subscribed.
 
@@ -107,7 +107,7 @@
 The subscribers portlet displays the new subscribed team.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(user_browser.contents)
     David Allouche (Unsubscribe)
@@ -134,14 +134,14 @@
     ...     visibility=PersonVisibility.PRIVATE)
     >>> logout()
     >>> foobar_browser = setupBrowser(auth='Basic foo.bar@xxxxxxxxxxxxx:test')
-    >>> foobar_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> foobar_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> foobar_browser.getLink('Subscribe someone else').click()
     >>> foobar_browser.getControl("Person").value = 'private-team'
     >>> foobar_browser.getControl("Subscribe user").click()
     >>> foobar_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
     >>> foobar_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(foobar_browser.contents)
     David Allouche (Unsubscribe)
@@ -154,7 +154,7 @@
 cannot unsubscribe them.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(user_browser.contents)
     David Allouche (Unsubscribe)
@@ -167,7 +167,7 @@
 list.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/bugs/1/'
+    ...     'http://bugs.launchpad.test/bugs/1/'
     ...     '+bug-portlet-subscribers-details')
     >>> print_direct_subscribers(anon_browser.contents)
     David Allouche
@@ -184,7 +184,7 @@
     ...     print(' | '.join(
     ...         extract_text(cell) for cell in row(('th', 'td'))))
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+activity')
+    ...     'http://bugs.launchpad.test/firefox/+bug/1/+activity')
     >>> main_content = find_main_content(user_browser.contents)
     >>> for row in main_content.table('tr'):
     ...     print_row(row)

=== modified file 'lib/lp/bugs/stories/bugs/xx-add-comment-bugtask-edit.txt'
--- lib/lp/bugs/stories/bugs/xx-add-comment-bugtask-edit.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-add-comment-bugtask-edit.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 the comment to another comment field.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+editstatus')
+    ...     'http://bugs.launchpad.test/firefox/+bug/1/+editstatus')
     >>> user_browser.getControl('Comment').value = (
     ...     'A comment with no change to the bug task.')
     >>> user_browser.getControl('Save Changes').click()
@@ -16,7 +16,7 @@
 The user was returned to the bug page, and the comment was added.
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
     >>> main_content = find_main_content(user_browser.contents)
     >>> last_comment = main_content('div', 'boardCommentBody')[-1]

=== modified file 'lib/lp/bugs/stories/bugs/xx-add-comment-distribution-no-current-release.txt'
--- lib/lp/bugs/stories/bugs/xx-add-comment-distribution-no-current-release.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-add-comment-distribution-no-current-release.txt	2019-05-22 15:20:07 +0000
@@ -3,13 +3,13 @@
 If a bug is reported distribution with no current release, like Gentoo,
 it's still possible to add comments to the bug.
 
-    >>> user_browser.open('http://launchpad.dev/gentoo/+filebug')
+    >>> user_browser.open('http://launchpad.test/gentoo/+filebug')
     >>> user_browser.getControl('Summary', index=0).value = 'Test bug'
     >>> user_browser.getControl('Continue').click()
     >>> user_browser.getControl('Further information').value = 'A test bug.'
     >>> user_browser.getControl('Submit Bug Report').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/gentoo/+bug/...'
+    'http://bugs.launchpad.test/gentoo/+bug/...'
 
     >>> user_browser.getControl(name='field.comment').value = 'A new comment.'
     >>> user_browser.getControl('Post Comment', index=-1).click()

=== modified file 'lib/lp/bugs/stories/bugs/xx-add-comment-with-bugwatch-and-cve.txt'
--- lib/lp/bugs/stories/bugs/xx-add-comment-with-bugwatch-and-cve.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-add-comment-with-bugwatch-and-cve.txt	2019-05-22 15:20:07 +0000
@@ -14,7 +14,7 @@
   >>> user_browser.getControl('Post Comment', index=-1).click()
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/1'
+  'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/1'
 
   >>> added_cve_link = user_browser.getLink('1991-9911')
   >>> print(added_cve_link)
@@ -42,7 +42,7 @@
   ...     u'is.'.encode('utf-8'))
   >>> user_browser.getControl('Post Comment', index=-1).click()
   >>> user_browser.url
-  'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/1'
+  'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/1'
 
   >>> bugwatch_portlet = find_portlet(user_browser.contents, 
   ...    'Remote bug watches')

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-activity.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-activity.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-activity.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 The bug activity page is where you find the "changelog" of a bug.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/debian/+source/'
+    ...     'http://bugs.launchpad.test/debian/+source/'
     ...     'mozilla-firefox/+bug/3/+activity')
     >>> main_content = find_main_content(anon_browser.contents)
 
@@ -14,12 +14,12 @@
     >>> print_location(anon_browser.contents)
     Hierarchy: Debian > mozilla-firefox package > Bug #3...
     Tabs:
-    * Overview - http://launchpad.dev/debian/+source/mozilla-firefox
-    * Code - http://code.launchpad.dev/debian/+source/mozilla-firefox
-    * Bugs (selected) - http://bugs...dev/debian/+source/mozilla-firefox
+    * Overview - http://launchpad.test/debian/+source/mozilla-firefox
+    * Code - http://code.launchpad.test/debian/+source/mozilla-firefox
+    * Bugs (selected) - http://bugs...test/debian/+source/mozilla-firefox
     * Blueprints - not linked
-    * Translations - http://translations.launchpad.dev/debian/+source/mozilla-firefox
-    * Answers - http://answers.launchpad.dev/debian/+source/mozilla-firefox
+    * Translations - http://translations.launchpad.test/debian/+source/mozilla-firefox
+    * Answers - http://answers.launchpad.test/debian/+source/mozilla-firefox
     Main heading: Activity log for bug #3
 
 The activity log itself is presented as a table.
@@ -38,7 +38,7 @@
 The bug page contains a link to the activity log.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/debian/+source/'
+    ...     'http://bugs.launchpad.test/debian/+source/'
     ...     'mozilla-firefox/+bug/3')
     >>> print(anon_browser.getLink('See full activity log').url)
     http://.../+bug/3/+activity
@@ -58,7 +58,7 @@
     ...         print('-' * 8)
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+addcomment')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+addcomment')
     >>> user_browser.getControl(name='field.comment').value = (
     ...     "Here's a comment for testing, like.")
     >>> user_browser.getControl('Post Comment').click()
@@ -78,7 +78,7 @@
     --------
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+edit')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+edit')
     >>> admin_browser.getControl('Summary').value = (
     ...     "A new title for this bug")
     >>> admin_browser.getControl('Change').click()
@@ -86,7 +86,7 @@
 Alterations to the summary of a bug will show up along with any comments
 that have been added.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/15')
+    >>> user_browser.open('http://launchpad.test/bugs/15')
     >>> print_comments(user_browser.contents, slice(None))
     In...
     Bug
@@ -102,12 +102,12 @@
 updated', since such changes can be quite long.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+edit')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+edit')
     >>> admin_browser.getControl("Description").value = (
     ...     "I've changed the description, isn't that excellent?")
     >>> admin_browser.getControl("Change").click()
 
-    >>> admin_browser.open('http://launchpad.dev/bugs/15')
+    >>> admin_browser.open('http://launchpad.test/bugs/15')
     >>> print_comments(admin_browser.contents)
     Foo Bar
     ... ago
@@ -121,11 +121,11 @@
 added.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+edit')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+edit')
     >>> admin_browser.getControl("Tags").value = "tag1 tag2 tag3"
     >>> admin_browser.getControl("Change").click()
 
-    >>> admin_browser.open('http://launchpad.dev/bugs/15')
+    >>> admin_browser.open('http://launchpad.test/bugs/15')
     >>> print_comments(admin_browser.contents)
     Foo Bar
     ... ago
@@ -140,11 +140,11 @@
 made.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+edit')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+edit')
     >>> admin_browser.getControl("Tags").value = "tag1 tag2 tag4"
     >>> admin_browser.getControl("Change").click()
 
-    >>> admin_browser.open('http://launchpad.dev/bugs/15')
+    >>> admin_browser.open('http://launchpad.test/bugs/15')
     >>> print_comments(admin_browser.contents)
     Foo Bar (name16)
     ... ago
@@ -163,12 +163,12 @@
 We'll add a milestone to Redfish to demonstrate this.
 
     >>> admin_browser.open(
-    ...     'http://launchpad.dev/redfish/trunk/+addmilestone')
+    ...     'http://launchpad.test/redfish/trunk/+addmilestone')
     >>> admin_browser.getControl('Name').value = 'foo'
     >>> admin_browser.getControl('Register Milestone').click()
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/redfish/+bug/15/+editstatus')
+    ...     'http://bugs.launchpad.test/redfish/+bug/15/+editstatus')
     >>> admin_browser.getControl('Status').value = ['Confirmed']
     >>> admin_browser.getControl('Importance').value = ['High']
     >>> admin_browser.getControl(
@@ -199,7 +199,7 @@
 package, the name of the package and the distro will be displayed.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/'
     ...     '1/+editstatus')
     >>> admin_browser.getControl('Status').value = ['Confirmed']
     >>> admin_browser.getControl("Save Changes").click()
@@ -216,7 +216,7 @@
 bundled with that comment in the UI.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/'
     ...     '1/+editstatus')
     >>> admin_browser.getControl('Status').value = ['New']
     >>> admin_browser.getControl('Importance').value = ['Low']
@@ -241,7 +241,7 @@
 If a target of a bug task is changed the old and new value will be shown.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/'
     ...     '1/+editstatus')
     >>> admin_browser.getControl(
     ...     name='ubuntu_mozilla-firefox.target.package'
@@ -260,11 +260,11 @@
 
 If a bug task is deleted the pillar no longer affected will be shown.
 
-    >>> admin_browser.open("http://bugs.launchpad.dev/firefox/+bug/6";)
+    >>> admin_browser.open("http://bugs.launchpad.test/firefox/+bug/6";)
     >>> admin_browser.getLink(url='+distrotask').click()
     >>> admin_browser.getControl('Distribution').value = ['ubuntu']
     >>> admin_browser.getControl('Continue').click()
-    >>> admin_browser.open("http://bugs.launchpad.dev/ubuntu/+bug/6/+delete";)
+    >>> admin_browser.open("http://bugs.launchpad.test/ubuntu/+bug/6/+delete";)
     >>> admin_browser.getControl('Delete').click()
     >>> print_comments(admin_browser.contents)
     Foo Bar (name16)
@@ -276,10 +276,10 @@
 Changes to information_type are shown.
 
     >>> admin_browser.open(
-    ...     "http://bugs.launchpad.dev/evolution/+bug/7/+secrecy";)
+    ...     "http://bugs.launchpad.test/evolution/+bug/7/+secrecy";)
     >>> admin_browser.getControl("Private", index=1).selected = True
     >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.open("http://bugs.launchpad.dev/evolution/+bug/7";)
+    >>> admin_browser.open("http://bugs.launchpad.test/evolution/+bug/7";)
     >>> print_comments(admin_browser.contents)
     Foo Bar (name16)
     ... ago
@@ -288,10 +288,10 @@
     --------
 
     >>> admin_browser.open(
-    ...     "http://bugs.launchpad.dev/jokosher/+bug/14/+secrecy";)
+    ...     "http://bugs.launchpad.test/jokosher/+bug/14/+secrecy";)
     >>> admin_browser.getControl("Private", index=1).selected = True
     >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.open("http://bugs.launchpad.dev/jokosher/+bug/14";)
+    >>> admin_browser.open("http://bugs.launchpad.test/jokosher/+bug/14";)
     >>> print_comments(admin_browser.contents)
     Foo Bar (name16)
     ... ago

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt	2019-05-22 15:20:07 +0000
@@ -32,7 +32,7 @@
 
    >>> user_browser.getLink(url='+affectsmetoo').click()
    >>> print(user_browser.url)
-   http://bugs.launchpad.dev/.../+bug/.../+affectsmetoo
+   http://bugs.launchpad.test/.../+bug/.../+affectsmetoo
    >>> user_browser.getControl(name='field.affects').value
    ['YES']
 

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 case, it means being logged in.
 
     >>> anon_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     Traceback (most recent call last):
       ..
     Unauthorized: ...launchpad.Edit...
@@ -13,12 +13,12 @@
 So let's login and add a comment.
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     >>> user_browser.getControl(name='field.comment').value = "a test comment"
     >>> user_browser.getControl("Post Comment").click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1
+    http://bugs.launchpad.test/firefox/+bug/1
 
     >>> print_feedback_messages(user_browser.contents)
     Thank you for your comment.
@@ -28,7 +28,7 @@
     >>> from StringIO import StringIO
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO("a test file"), "text/plain", "foo.txt")
     >>> user_browser.getControl("Post Comment").click()
@@ -39,7 +39,7 @@
 A comment and attachment can be submitted in one request.
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     >>> user_browser.getControl(
     ...     name='field.comment').value = "this is a comment"
     >>> user_browser.getControl("Attachment").add_file(
@@ -48,7 +48,7 @@
     >>> user_browser.getControl("Post Comment").click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1
+    http://bugs.launchpad.test/firefox/+bug/1
 
     >>> print_feedback_messages(user_browser.contents)
     Thank you for your comment.
@@ -57,13 +57,13 @@
 You cannot upload an empty attachment.
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO(""), "text/plain", "foo.txt")
     >>> user_browser.getControl("Post Comment").click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1/+addcomment
+    http://bugs.launchpad.test/firefox/+bug/1/+addcomment
 
     >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
@@ -78,7 +78,7 @@
     1024
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment-form";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment-form";)
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO("x"*1025), "text/plain", "foo.txt")
     >>> user_browser.getControl("Post Comment").click()
@@ -90,14 +90,14 @@
 The comment/attach file form is available from a link on the bug page.
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1/+addcomment";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1/+addcomment";)
     >>> user_browser.getControl("Attachment").add_file(
     ...     StringIO("a test file"), "text/plain", "foo.txt")
     >>> user_browser.getControl("Description").value = "some file"
     >>> user_browser.getControl("Post Comment").click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1
+    http://bugs.launchpad.test/firefox/+bug/1
 
     >>> print_feedback_messages(user_browser.contents)
     Attachment foo.txt added to bug.

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2019-05-22 15:20:07 +0000
@@ -32,7 +32,7 @@
     ...         print(div_tag)
     >>> print_comments(browser.contents) #doctest: -ELLIPSIS
     <div class="boardCommentBody">
-    <a href="http://bugs.launchpad.dev/tomcat/+bug/2/comments/1/+download";>Download
+    <a href="http://bugs.launchpad.test/tomcat/+bug/2/comments/1/+download";>Download
     full text</a> (363 bytes)
     <div class="comment-text" itemprop="commentText"><p>This
     would be a real killer feature. If there...</p></div>
@@ -41,7 +41,7 @@
     </p>
     </div>
     <div class="boardCommentBody">
-    <a href="http://bugs.launchpad.dev/tomcat/+bug/2/comments/2/+download";>Download
+    <a href="http://bugs.launchpad.test/tomcat/+bug/2/comments/2/+download";>Download
     full text</a> (364 bytes)
     <div class="comment-text" itemprop="commentText"><p>Oddly
     enough the bug system seems only capabl...</p></div>
@@ -54,7 +54,7 @@
 
     >>> browser.getLink("Read more...").click()
     >>> browser.url
-    'http://bugs.launchpad.dev/tomcat/+bug/2/comments/1'
+    'http://bugs.launchpad.test/tomcat/+bug/2/comments/1'
 
 The whole comment is visible on this page:
 
@@ -85,7 +85,7 @@
 the same rules as xx-question-message.txt; changes here may require
 changes to that test.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/tomcat/+bug/2')
+    >>> user_browser.open('http://bugs.launchpad.test/tomcat/+bug/2')
     >>> user_browser.title.decode('utf-8')
     u'Bug #2 (blackhole) ... : Bugs : Tomcat'
     >>> user_browser.getControl(name='field.comment').value = (
@@ -133,7 +133,7 @@
 address, '<email address hidden>'. The anonymous user is
 unauthenticated, so they will see the obfuscated email address.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/tomcat/+bug/2')
+    >>> anon_browser.open('http://bugs.launchpad.test/tomcat/+bug/2')
     >>> anon_browser.title.decode('utf-8')
     u'Bug #2 (blackhole) ... : Bugs : Tomcat'
     >>> text = find_tags_by_class(

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-create-question.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2019-05-22 15:20:07 +0000
@@ -11,7 +11,7 @@
 An anonymous user cannot create a question.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/ubuntu/+source/linux-source-2.6.15/+bug/10')
     >>> anon_browser.title
     'Bug #10 ... : Bugs : linux-source-2.6.15 package : Ubuntu'
@@ -25,7 +25,7 @@
 really a question. They choose to make a question from it.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/ubuntu/+source/linux-source-2.6.15/+bug/10')
     >>> user_browser.title
     'Bug #10 ... : Bugs : linux-source-2.6.15 package : Ubuntu'
@@ -68,7 +68,7 @@
 bugtask directly.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/ubuntu/+source/linux-source-2.6.15/+bug/10/+editstatus')
     >>> print_feedback_messages(user_browser.contents)
     This bug was converted into a question. It cannot be edited.
@@ -91,7 +91,7 @@
 questions' portlet, and uses it to go to the question page.
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/ubuntu/.../+bug/10
+    http://bugs.launchpad.test/ubuntu/.../+bug/10
 
     >>> portlet = find_portlet(user_browser.contents, 'Related questions')
     >>> question_anchor = portlet.a
@@ -124,7 +124,7 @@
 Actions menu, the page explains why they cannot make the bug into a
 question.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/thunderbird/+bug/9')
+    >>> user_browser.open('http://bugs.launchpad.test/thunderbird/+bug/9')
     >>> user_browser.title
     'Bug #9 ...'
 
@@ -156,7 +156,7 @@
 a bookmark, they see that they cannot create the question again.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/ubuntu/+source/linux-source-2.6.15/+bug/10/+create-question')
     >>> user_browser.title
     'Convert this bug to a question...
@@ -188,7 +188,7 @@
     >>> logout()
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/12')
+    ...     'http://bugs.launchpad.test/jokosher/+bug/12')
     >>> user_browser.title
     'Bug #12 ...'
 
@@ -283,7 +283,7 @@
 remove.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/12/+remove-question')
+    ...     'http://bugs.launchpad.test/jokosher/+bug/12/+remove-question')
     >>> print(user_browser.title)
     Bug #12 - Convert this...
 

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-edit.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-edit.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-edit.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 see the bug number being edited.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/11/+edit')
+    ...     'http://bugs.launchpad.test/jokosher/+bug/11/+edit')
     >>> print(user_browser.title)
     Edit details for bug #11 ...
     >>> main = find_main_content(user_browser.contents)
@@ -16,10 +16,10 @@
 
     >>> browser = setupBrowser(auth='Basic no-priv@xxxxxxxxxxxxx:test')
     >>> browser.open(
-    ...     'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/3')
+    ...     'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/3')
     >>> browser.getLink(url="+edit").click()
     >>> browser.url
-    'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/3/+edit'
+    'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/3/+edit'
     >>> browser.getControl('Summary').value = 'New Summary'
     >>> browser.getControl('Description').value = 'New description.'
     >>> browser.getControl('Change').click()
@@ -38,7 +38,7 @@
 If a bug's description has not been changed since it was reported,
 there is no link to the original description.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/4')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/4')
     >>> user_browser.getLink('original description')
     Traceback (most recent call last):
     ...
@@ -60,10 +60,10 @@
 When editing bug tags, we want to discourage people from adding new
 tags.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> user_browser.getLink(url="+edit").click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1/+edit'
+    'http://bugs.launchpad.test/firefox/+bug/1/+edit'
 
     >>> user_browser.getControl('Tags').value = 'layout-test'
     >>> user_browser.getControl('Change').click()
@@ -71,7 +71,7 @@
 Now we are back at the bug page, and the tag has been added.
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
     >>> b'layout-test' in user_browser.contents
     True
     >>> b'new-tag' in user_browser.contents
@@ -84,7 +84,7 @@
     >>> user_browser.getControl('Change').click()
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1'
+    'http://bugs.launchpad.test/firefox/+bug/1'
 
     >>> tags_div = extract_text(
     ...       find_tag_by_id(user_browser.contents, 'bug-tags'))

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt	2019-05-22 15:20:07 +0000
@@ -2,7 +2,7 @@
 
 Bug heat appears on the bug index page:
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> content = find_main_content(anon_browser.contents)
     >>> print(content.find('a', href='/+help-bugs/bug-heat.html'))
     <a href="/+help-bugs/bug-heat.html" target="help" class="sprite flame">0</a>

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 All comments are set visible by default.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11')
     >>> user_browser.getControl(name='field.comment').value = (
     ...     'This comment will not be visible when the test completes.')
@@ -39,7 +39,7 @@
 
     >>> test_browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
     >>> test_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11')
     >>> main_content = find_main_content(test_browser.contents)
     >>> last_comment = main_content('div', 'boardCommentBody')[-1]
@@ -56,7 +56,7 @@
     ...     number_node = comment.find(None, 'bug-comment-index')
     ...     latest_index = extract_text(number_node)
     >>> test_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11/comments/%d' % (int(latest_index[1:]) + 1))
     Traceback (most recent call last):
     ...
@@ -65,7 +65,7 @@
 For admin users, the message is still visible in the bug page.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11')
     >>> main_content = find_main_content(admin_browser.contents)
     >>> last_comment = main_content('div', 'boardCommentBody')[-1]
@@ -83,7 +83,7 @@
 highlighted with the 'adminHiddenComment style there too.
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11/comments/%d' % (int(latest_index[1:]) + 1))
     >>> contents = extract_text(admin_browser.contents)
     >>> print(contents)
@@ -99,7 +99,7 @@
 Also for the owner of comment the message is still visible in the bug page.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11')
     >>> main_content = find_main_content(user_browser.contents)
     >>> last_comment = main_content('div', 'boardCommentBody')[-1]
@@ -117,7 +117,7 @@
 highlighted with the 'adminHiddenComment style there too.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/jokosher/+bug/11/comments/%d' % (int(latest_index[1:]) + 1))
     >>> contents = extract_text(user_browser.contents)
     >>> print(contents)

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-index-lots-of-comments.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-index-lots-of-comments.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-index-lots-of-comments.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 has the comment-text CSS class, increasing all the find_tags_by_class counts
 below by one.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/11')
+    >>> user_browser.open('http://launchpad.test/bugs/11')
     >>> comments = find_tags_by_class(user_browser.contents, 'comment-text')
     >>> len(comments)
     7
@@ -27,7 +27,7 @@
 
 Now only 3 comments will be displayed; the oldest and the 2 newest.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/11')
+    >>> user_browser.open('http://launchpad.test/bugs/11')
     >>> comments = find_tags_by_class(user_browser.contents, 'comment-text')
     >>> len(comments)
     4
@@ -72,7 +72,7 @@
 list is truncated, the usual "to post a comment you must log in" note
 is also removed.
 
-    >>> anon_browser.open('http://launchpad.dev/bugs/11')
+    >>> anon_browser.open('http://launchpad.test/bugs/11')
     >>> print(find_tag_by_id(
     ...     anon_browser.contents, 'add-comment-login-first'))
     None

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-index.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-index.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-index.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 to be fixed, and so forth.
 
     >>> anon_browser.open(
-    ...     "http://launchpad.dev/debian/+source/mozilla-firefox/+bug/2";)
+    ...     "http://launchpad.test/debian/+source/mozilla-firefox/+bug/2";)
     >>> anon_browser.title
     '...Blackhole Trash folder...'
 
@@ -16,20 +16,20 @@
     >>> print_location(anon_browser.contents)
     Hierarchy: Debian > mozilla-firefox package
     Tabs:
-    * Overview - http://launchpad.dev/debian/+source/mozilla-firefox
-    * Code - http://code.launchpad.dev/debian/+source/mozilla-firefox
+    * Overview - http://launchpad.test/debian/+source/mozilla-firefox
+    * Code - http://code.launchpad.test/debian/+source/mozilla-firefox
     * Bugs (selected) -
-      http://bugs.launchpad.dev/debian/+source/mozilla-firefox
+      http://bugs.launchpad.test/debian/+source/mozilla-firefox
     * Blueprints - not linked
-    * Translations - http://translations.launchpad.dev/debian/+source/mozilla-firefox
-    * Answers - http://answers.launchpad.dev/debian/+source/mozilla-firefox
+    * Translations - http://translations.launchpad.test/debian/+source/mozilla-firefox
+    * Answers - http://answers.launchpad.test/debian/+source/mozilla-firefox
     Main heading: Blackhole Trash folder
 
 The page features a table of places where people have asked for the bug
 to be fixed. The row for the current context (and not any other row) is
 highlighted.
 
-    >>> anon_browser.open('http://launchpad.dev/firefox/+bug/1')
+    >>> anon_browser.open('http://launchpad.test/firefox/+bug/1')
     >>> print(anon_browser.contents)
     <!DOCTYPE...
     ...
@@ -46,7 +46,7 @@
     1
 
     >>> anon_browser.open(
-    ...     'http://launchpad.dev/debian/+source/mozilla-firefox/+bug/1')
+    ...     'http://launchpad.test/debian/+source/mozilla-firefox/+bug/1')
     >>> print(anon_browser.contents)
     <!DOCTYPE...
     ...
@@ -79,7 +79,7 @@
 status in that context. The forms are hidden initially using CSS.
 
     >>> user_browser.open(
-    ...     "http://launchpad.dev/debian/+source/mozilla-firefox/+bug/2";)
+    ...     "http://launchpad.test/debian/+source/mozilla-firefox/+bug/2";)
     >>> print(user_browser.contents)
     <!DOCTYPE...
     ...
@@ -99,7 +99,7 @@
 including the latest release if it is a distribution package.
 
     >>> user_browser.open(
-    ...     "http://launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1";)
+    ...     "http://launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1";)
     >>> print(extract_text(find_tag_by_id(user_browser.contents, 'task17')))
     Affecting: mozilla-firefox (Ubuntu)
     Filed here by: Foo Bar
@@ -109,20 +109,20 @@
 
 The bug page includes a link to report another bug.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> print(user_browser.getLink('Report a bug').url)
-    http://bugs.launchpad.dev/firefox/+filebug
+    http://bugs.launchpad.test/firefox/+filebug
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1')
+    ...     'http://launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1')
     >>> print(user_browser.getLink('Report a bug').url)
-    http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug
+    http://launchpad.test/ubuntu/+source/mozilla-firefox/+filebug
 
 There's also a link on the page that will take the user to the "Add
 attachment or patch" page, for use when JavaScript isn't available.
 
     >>> print(user_browser.getLink('Add attachment or patch').url)
-    http://bugs.launchpad.dev/ubuntu/+source/.../+bug/1/+addcomment
+    http://bugs.launchpad.test/ubuntu/+source/.../+bug/1/+addcomment
 
 
 Navigating to a bug in the wrong context
@@ -131,9 +131,9 @@
 If we navigate to a bug in a context in which it doesn't exist, we are
 redirected to the default context.
 
-    >>> browser.open('http://bugs.launchpad.dev/jokosher/+bug/10')
+    >>> browser.open('http://bugs.launchpad.test/jokosher/+bug/10')
     >>> print(browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/linux-source-2.6.15/+bug/10
+    http://bugs.launchpad.test/ubuntu/+source/linux-source-2.6.15/+bug/10
 
 
 Bugs with many tasks

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt	2019-05-22 15:20:07 +0000
@@ -29,7 +29,7 @@
     >>> logout()
 
     >>> user_browser.open(
-    ...     "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/";
+    ...     "http://launchpad.test/distros/ubuntu/+source/mozilla-firefox/";
     ...     "+bug/1/nominations/2/+bugtasks-and-nominations-table-row")
 
 no-priv will, of course, see their nomination.
@@ -53,7 +53,7 @@
 buttons) directly either.
 
     >>> user_browser.open(
-    ...     "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/";
+    ...     "http://launchpad.test/distros/ubuntu/+source/mozilla-firefox/";
     ...     "+bug/1/nominations/2/+edit-form")
     Traceback (most recent call last):
       ...
@@ -74,7 +74,7 @@
 
     >>> browser = setupBrowser("Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open(
-    ...     "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/";
+    ...     "http://launchpad.test/distros/ubuntu/+source/mozilla-firefox/";
     ...     "+bug/1/nominations/2/+bugtasks-and-nominations-table-row")
 
     >>> approve_button = browser.getControl("Approve")
@@ -94,7 +94,7 @@
 
     >>> browser = setupBrowser("Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open(
-    ...     "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/";
+    ...     "http://launchpad.test/distros/ubuntu/+source/mozilla-firefox/";
     ...     "+bug/1/nominations/2/+bugtasks-and-nominations-table-row")
 
     >>> browser.contents

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 description in the bug page.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/debian/sarge/+source/mozilla-firefox/+bug/3')
     >>> user_browser.title
     'Bug #3 ...'
@@ -23,7 +23,7 @@
 An anonymous cannot see the email address anywhere in the page.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev'
+    ...     'http://bugs.launchpad.test'
     ...     '/debian/sarge/+source/mozilla-firefox/+bug/3')
     >>> print(anon_browser.title)
     Bug #3 ...

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-single-comment-view.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-single-comment-view.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-single-comment-view.txt	2019-05-22 15:20:07 +0000
@@ -1,9 +1,9 @@
 Each bug comment on the main bug page contains a link to view only that
 comment.
 
-    >>> browser.open('http://bugs.launchpad.dev/bugs/2')
+    >>> browser.open('http://bugs.launchpad.test/bugs/2')
     >>> browser.url
-    'http://bugs.launchpad.dev/tomcat/+bug/2'
+    'http://bugs.launchpad.test/tomcat/+bug/2'
 
     >>> def print_comment_titles(page):
     ...     """Prints all the comment titles that are visible on the page."""
@@ -22,7 +22,7 @@
     >>> comment_link = browser.getLink('Strange bug with duplicate messages.')
     >>> comment_link.click()
     >>> browser.url
-    'http://bugs.launchpad.dev/tomcat/+bug/2/comments/2'
+    'http://bugs.launchpad.test/tomcat/+bug/2/comments/2'
 
     >>> print_comment_titles(browser.contents)
     Strange bug with duplicate messages.

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-text-pages.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-text-pages.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-text-pages.txt	2019-05-22 15:20:07 +0000
@@ -48,13 +48,13 @@
 Users can view a textual description of any bug at that bug's text page,
 according to the following URL pattern:
 
-    http://launchpad.dev/bugs/<bug-number>/+text
+    http://launchpad.test/bugs/<bug-number>/+text
 
 For example, users can view a textual description of bug 1:
 
-    >>> anon_browser.open('http://launchpad.dev/bugs/1/+text')
+    >>> anon_browser.open('http://launchpad.test/bugs/1/+text')
     >>> anon_browser.url
-    'http://launchpad.dev/bugs/1/+text'
+    'http://launchpad.test/bugs/1/+text'
     >>> print(anon_browser.headers['content-type'])
     text/plain;charset=utf-8 
 
@@ -71,8 +71,8 @@
     duplicate-of: 
     duplicates: 
     attachments:
-       http://bugs.launchpad.dev/.../+files/file_a.txt text/html
-       http://bugs.launchpad.dev/.../+files/file%20with%20space.txt
+       http://bugs.launchpad.test/.../+files/file_a.txt text/html
+       http://bugs.launchpad.test/.../+files/file%20with%20space.txt
          text/plain; name="file with space.txt"
     patches:
         http://.../bug-patch.diff text/plain
@@ -151,7 +151,7 @@
     >>> attachments_text = text_bug[text_bug.find('attachments:'):]
     >>> attachment_2 = attachments_text.split('\n')[2]
     >>> attachment_2
-    u' http://bugs.launchpad.dev/.../file%20with%20space.txt text/plain;
+    u' http://bugs.launchpad.test/.../file%20with%20space.txt text/plain;
     name="file with space.txt"'
 
 
@@ -160,14 +160,14 @@
 Users can also view a textual description of a bug from the context of a task
 relating to that bug, according to the following URL pattern:
 
-   http://launchpad.dev/<target>/+bug/<number>/+text
+   http://launchpad.test/<target>/+bug/<number>/+text
 
 For example, since bug 1 affects Mozilla Firefox, users can view the textual
 description of bug 1 directly from the Mozilla Firefox-specific text page:
 
-    >>> anon_browser.open('http://launchpad.dev/firefox/+bug/1/+text')
+    >>> anon_browser.open('http://launchpad.test/firefox/+bug/1/+text')
     >>> anon_browser.url
-    'http://launchpad.dev/firefox/+bug/1/+text'
+    'http://launchpad.test/firefox/+bug/1/+text'
 
     >>> print(anon_browser.headers['content-type'])
     text/plain;charset=utf-8
@@ -215,21 +215,21 @@
     ...             if bug_lines[line_no] != bug_task_lines[line_no]:
     ...                 print(bug_lines[line_no])
     ...                 print(bug_task_lines[line_no])
-    http://bugs.launchpad.dev/bugs/1/+attachment/.../+files/file_a.txt text/html
-    http://bugs.launchpad.dev/firefox/+bug/.../+files/file_a.txt text/html
-    http://bugs.launchpad.dev/bugs/1/.../+files/file%20with%20space.txt...
-    http://bugs.launchpad.dev/firefox/+bug/.../+files/file%20with%20space.txt...
-    http://bugs.launchpad.dev/bugs/1/.../+files/bug-patch.diff text/plain
-    http://bugs.launchpad.dev/firefox/+bug/.../+files/bug-patch.diff text/plain
+    http://bugs.launchpad.test/bugs/1/+attachment/.../+files/file_a.txt text/html
+    http://bugs.launchpad.test/firefox/+bug/.../+files/file_a.txt text/html
+    http://bugs.launchpad.test/bugs/1/.../+files/file%20with%20space.txt...
+    http://bugs.launchpad.test/firefox/+bug/.../+files/file%20with%20space.txt...
+    http://bugs.launchpad.test/bugs/1/.../+files/bug-patch.diff text/plain
+    http://bugs.launchpad.test/firefox/+bug/.../+files/bug-patch.diff text/plain
 
 == Duplicate Bugs ==
 
 When one bug duplicates another bug, the textual description includes the
 duplicated bug's ID:
 
-    >>> anon_browser.open('http://launchpad.dev/bugs/6/+text')
+    >>> anon_browser.open('http://launchpad.test/bugs/6/+text')
     >>> anon_browser.url
-    'http://launchpad.dev/bugs/6/+text'
+    'http://launchpad.test/bugs/6/+text'
     >>> print(anon_browser.headers['content-type'])
     text/plain;charset=utf-8 
 
@@ -242,9 +242,9 @@
 When a bug has duplicate bugs, the textual description includes a list of the
 duplicate bug IDs:
 
-    >>> anon_browser.open('http://launchpad.dev/bugs/5/+text')
+    >>> anon_browser.open('http://launchpad.test/bugs/5/+text')
     >>> anon_browser.url
-    'http://launchpad.dev/bugs/5/+text'
+    'http://launchpad.test/bugs/5/+text'
     >>> print(anon_browser.headers['content-type'])
     text/plain;charset=utf-8 
 
@@ -261,13 +261,13 @@
 Users can also see a list of all bug IDs for a given target by viewing that
 product's bugs text page, according to the following URL pattern:
 
-   http://launchpad.dev/<target>/+bugs-text
+   http://launchpad.test/<target>/+bugs-text
 
 For example, users can see the IDs of open bugs on Mozilla Firefox:
 
-    >>> anon_browser.open('http://launchpad.dev/firefox/+bugs-text')
+    >>> anon_browser.open('http://launchpad.test/firefox/+bugs-text')
     >>> anon_browser.url
-    'http://launchpad.dev/firefox/+bugs-text'
+    'http://launchpad.test/firefox/+bugs-text'
     >>> print(anon_browser.headers['content-type'])
     text/plain;charset=utf-8 
 
@@ -279,7 +279,7 @@
 graphical bugs page. To perform an advanced search, users can append any
 of the standard set of search parameters to a textual bugs page URL:
 
-    >>> base_url = 'http://launchpad.dev/firefox/+bugs-text'
+    >>> base_url = 'http://launchpad.test/firefox/+bugs-text'
     >>> search_parameters = 'field.status:list=FIXRELEASED'
     >>> url = base_url + '?' + search_parameters
     >>> anon_browser.open(url)
@@ -291,7 +291,7 @@
 
 Searching for bugs in a component of a distribution works too.
 
-    >>> base_url = 'http://launchpad.dev/ubuntu/+bugs-text'
+    >>> base_url = 'http://launchpad.test/ubuntu/+bugs-text'
     >>> search_parameters = 'field.component=1'
     >>> url = base_url + '?' + search_parameters
     >>> anon_browser.open(url)
@@ -303,7 +303,7 @@
 
 This page is also available for project groups.
 
-    >>> anon_browser.open('http://launchpad.dev/mozilla/+bugs-text')
+    >>> anon_browser.open('http://launchpad.test/mozilla/+bugs-text')
     >>> print(anon_browser.contents)
     15
     5
@@ -314,7 +314,7 @@
 
 When a bug is private, the textual description reflects this:
 
-    >>> admin_browser.open('http://launchpad.dev/bugs/14/+text')
+    >>> admin_browser.open('http://launchpad.test/bugs/14/+text')
     >>> print(admin_browser.contents)
     bug: 14
     title: jokosher exposes personal details in its actions portlet

=== modified file 'lib/lp/bugs/stories/bugs/xx-bugs-advanced-search-upstream-status.txt'
--- lib/lp/bugs/stories/bugs/xx-bugs-advanced-search-upstream-status.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bugs-advanced-search-upstream-status.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 There are currently four bugs open in Ubuntu.
 
     >>> from lp.bugs.tests.bug import print_bugtasks
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs')
     >>> print_bugtasks(anon_browser.contents)
     1 Firefox does not support SVG mozilla-firefox (Ubuntu) Medium New
     9 Thunderbird crashes thunderbird (Ubuntu) Medium Confirmed
@@ -22,7 +22,7 @@
     >>> anon_browser.getLink('Advanced search').attrs['href']
     '?advanced=1'
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> upstream_status = anon_browser.getControl(
     ...     name='field.status_upstream')
     >>> upstream_status.displayValue = [
@@ -60,7 +60,7 @@
 
     >>> logout()
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> upstream_status = anon_browser.getControl(
     ...     name='field.status_upstream')
     >>> upstream_status.displayValue = [
@@ -75,7 +75,7 @@
 makes it easy for developers to ignore bugs that they've already passed
 on upstream, to focus instead on things they need to work on.
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> upstream_status = anon_browser.getControl(
     ...     name='field.status_upstream')
     >>> upstream_status.displayValue = [
@@ -106,7 +106,7 @@
 Now, if we choose to show only closed upstream task, only the bugs we
 modified and the sampledata we created above will show up.
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> upstream_status = anon_browser.getControl(
     ...     name='field.status_upstream')
     >>> upstream_status.displayValue = [
@@ -120,7 +120,7 @@
 If more than one filter for upstream status is selected, the search
 returns the union of the results for the individual filters.
 
-    >>> anon_browser.open('http://launchpad.dev/ubuntu/+bugs?advanced=1')
+    >>> anon_browser.open('http://launchpad.test/ubuntu/+bugs?advanced=1')
     >>> upstream_status = anon_browser.getControl(
     ...     name='field.status_upstream')
     >>> upstream_status.displayValue = [
@@ -166,7 +166,7 @@
     ...     'field.has_no_package.used': '',
     ...     'search': 'Search'}
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+bugs?' + urlencode(
+    ...     'http://bugs.launchpad.test/ubuntu/+bugs?' + urlencode(
     ...         bookmark_params, True))
     >>> print_bugtasks(anon_browser.contents)
     1 Firefox does not support SVG mozilla-firefox (Ubuntu) Medium New
@@ -179,7 +179,7 @@
 
     >>> bookmark_params['field.status_upstream'] = 'pending_bugwatch'
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+bugs?' + urlencode(
+    ...     'http://bugs.launchpad.test/ubuntu/+bugs?' + urlencode(
     ...         bookmark_params, True))
     >>> print_bugtasks(anon_browser.contents)
 
@@ -188,7 +188,7 @@
 
     >>> bookmark_params['field.status_upstream'] = 'hide_upstream'
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+bugs?' + urlencode(
+    ...     'http://bugs.launchpad.test/ubuntu/+bugs?' + urlencode(
     ...         bookmark_params, True))
     >>> print_bugtasks(anon_browser.contents)
     10 another test bug linux-source-2.6.15 (Ubuntu) Medium New
@@ -198,7 +198,7 @@
 
     >>> bookmark_params['field.status_upstream'] = 'only_resolved_upstream'
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+bugs?' + urlencode(
+    ...     'http://bugs.launchpad.test/ubuntu/+bugs?' + urlencode(
     ...         bookmark_params, True))
     >>> print_bugtasks(anon_browser.contents)
     1 Firefox does not support SVG mozilla-firefox (Ubuntu) Medium New
@@ -209,7 +209,7 @@
 
     >>> bookmark_params['field.status_upstream'] = 'invalid'
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+bugs?' + urlencode(
+    ...     'http://bugs.launchpad.test/ubuntu/+bugs?' + urlencode(
     ...         bookmark_params, True))
     Traceback (most recent call last):
     ...

=== modified file 'lib/lp/bugs/stories/bugs/xx-bugs.txt'
--- lib/lp/bugs/stories/bugs/xx-bugs.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bugs.txt	2019-05-22 15:20:07 +0000
@@ -31,7 +31,7 @@
   >>> user_browser.getControl('Post Comment', index=-1).click()
 
   >>> user_browser.url
-  'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/2'
+  'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/2'
 
   >>> print(user_browser.contents)
   <...

=== modified file 'lib/lp/bugs/stories/bugs/xx-bugtask-assignee-widget.txt'
--- lib/lp/bugs/stories/bugs/xx-bugtask-assignee-widget.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-bugtask-assignee-widget.txt	2019-05-22 15:20:07 +0000
@@ -17,7 +17,7 @@
   >>> print(http(br"""
   ... POST /ubuntu/+source/mozilla-firefox/+bug/1/+editstatus HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Type: multipart/form-data; boundary=---------------------------19759086281403130373932339922
   ... 
   ... -----------------------------19759086281403130373932339922
@@ -90,7 +90,7 @@
   >>> print(http(br"""
   ... POST /ubuntu/+source/mozilla-firefox/+bug/1/+editstatus HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Length: 1999
   ... Content-Type: multipart/form-data; boundary=---------------------------19759086281403130373932339922
   ... 
@@ -164,7 +164,7 @@
   >>> print(http(br"""
   ... POST /ubuntu/+source/mozilla-firefox/+bug/1/+editstatus HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... Referer: https://launchpad.dev/
+  ... Referer: https://launchpad.test/
   ... Content-Length: 1999
   ... Content-Type: multipart/form-data; boundary=---------------------------19759086281403130373932339922
   ... 

=== modified file 'lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 The front page for a distribution on the bugs domain presents some basic
 information the bugs in it. It also displays the list of bugs.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+bugs')
     >>> anon_browser.title
     'Bugs : Ubuntu'
     >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
@@ -14,16 +14,16 @@
 
     >>> anon_browser.getLink('Open bugs').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bugs'
+    'http://bugs.launchpad.test/ubuntu/+bugs'
     >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
     True
 
 It also has a link to subscribe to bug mail.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/ubuntu/+bugs')
+    >>> user_browser.open('http://bugs.launchpad.test/ubuntu/+bugs')
     >>> user_browser.getLink('Subscribe to bug mail').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+subscribe
+    http://bugs.launchpad.test/ubuntu/+subscribe
 
 
 Bugs Fixed Elsewhere
@@ -32,7 +32,7 @@
 The Bugs frontpage for a distribution includes the number of bugs that are
 fixed in some other context.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+bugs')
     >>> fixed_elsewhere_link = anon_browser.getLink('Bugs fixed elsewhere')
 
 The link takes you to the list of the bugs fixed elsewhere.
@@ -52,7 +52,7 @@
 
 It also displays the number of open bugs associated with a CVE.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+bugs')
     >>> cve_bugs_link = anon_browser.getLink('Open CVE bugs')
 
 The link takes you to the list of bugs with CVEs linked to them.
@@ -70,7 +70,7 @@
 The bugs page displays the number of Incomplete, unattended bugs that
 can expire when the project has enabled bug expiration.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+bugs')
     >>> expirable_bugs_link = anon_browser.getLink(
     ...     'Incomplete bugs')
 
@@ -84,7 +84,7 @@
 Debian does not use Launchpad to track bugs; the anonymous user cannot
 see any link reporting that bugs can expire.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/debian/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/debian/+bugs')
     >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
     Traceback (most recent call last):
     ...

=== modified file 'lib/lp/bugs/stories/bugs/xx-distributionsourcepackage-bugs.txt'
--- lib/lp/bugs/stories/bugs/xx-distributionsourcepackage-bugs.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-distributionsourcepackage-bugs.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 is present on the page.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/alsa-utils/')
+    ...     'http://bugs.launchpad.test/ubuntu/+source/alsa-utils/')
     >>> portlet = find_portlet(anon_browser.contents,
     ...     '"alsa-utils" versions published in Ubuntu')
     >>> print(portlet)

=== modified file 'lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 The +bugs page for a distribution series presents some basic information the
 bugs, as well as a listing.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/warty/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty/+bugs')
     >>> anon_browser.title
     'Warty (4.10) : Bugs : Ubuntu'
 
@@ -14,16 +14,16 @@
 
     >>> anon_browser.getLink('Open bugs').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/ubuntu/warty/+bugs'
+    'http://bugs.launchpad.test/ubuntu/warty/+bugs'
     >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
     True
 
 It also has a link to subscribe to bug mail.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/ubuntu/warty')
+    >>> user_browser.open('http://bugs.launchpad.test/ubuntu/warty')
     >>> user_browser.getLink('Subscribe to bug mail').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/ubuntu/warty/+subscribe
+    http://bugs.launchpad.test/ubuntu/warty/+subscribe
 
 
 == Bugs Fixed Elsewhere ==
@@ -31,7 +31,7 @@
 The Bugs frontpage includes the number of bugs that are fixed in some
 other context.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/warty')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty')
     >>> fixed_elsewhere_link = anon_browser.getLink('Bugs fixed elsewhere')
 
 The link takes you to the list of the bugs fixed elsewhere.
@@ -51,7 +51,7 @@
 The bugs page displays the number of Incomplete, unattended bugs that
 can expire when the project has enabled bug expiration.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/warty')
+    >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty')
     >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
 
 The link goes to the expirable bugs page, where the anonymous user can

=== modified file 'lib/lp/bugs/stories/bugs/xx-duplicate-of-private-bug.txt'
--- lib/lp/bugs/stories/bugs/xx-duplicate-of-private-bug.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-duplicate-of-private-bug.txt	2019-05-22 15:20:07 +0000
@@ -7,12 +7,12 @@
 First we mark a bug as private:
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/'
+    ...     'http://bugs.launchpad.test/'
     ...         'debian/+source/mozilla-firefox/+bug/8/+secrecy')
     >>> admin_browser.getControl('Private', index=1).selected = True
     >>> admin_browser.getControl('Change').click()
     >>> print(admin_browser.url)
-    http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/8
+    http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/8
 
     >>> print(admin_browser.title)
     Bug #8 ...Printing doesn't work...
@@ -23,7 +23,7 @@
 Next we mark another bug as a duplicate of the private bug:
 
     >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/'
+    ...     'http://bugs.launchpad.test/'
     ...         'ubuntu/+source/linux-source-2.6.15/+bug/10/+duplicate')
     >>> admin_browser.getControl('Duplicate Of').value = '8'
     >>> admin_browser.getControl('Set Duplicate').click()

=== modified file 'lib/lp/bugs/stories/bugs/xx-edit-no-currentrelease-distribution-task.txt'
--- lib/lp/bugs/stories/bugs/xx-edit-no-currentrelease-distribution-task.txt	2014-04-04 15:56:01 +0000
+++ lib/lp/bugs/stories/bugs/xx-edit-no-currentrelease-distribution-task.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 still possible to edit its tasks from the bug page. There are no Gentoo
 tasks currently open, so let's add one.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/4')
+    >>> user_browser.open('http://launchpad.test/bugs/4')
     >>> user_browser.getLink('Also affects distribution/package').click()
 
     >>> user_browser.getControl('Distribution').value = ['gentoo']

=== modified file 'lib/lp/bugs/stories/bugs/xx-front-page-bug-lists.txt'
--- lib/lp/bugs/stories/bugs/xx-front-page-bug-lists.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-front-page-bug-lists.txt	2019-05-22 15:20:07 +0000
@@ -38,7 +38,7 @@
 
 The bugs front page can be consulted now for the list of reported bugs.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> reported_bugs = find_tag_by_id(anon_browser.contents, 'reported-bugs')
 
 The list of recently reported bugs contains up to the last 5 bugs reported

=== modified file 'lib/lp/bugs/stories/bugs/xx-front-page-info.txt'
--- lib/lp/bugs/stories/bugs/xx-front-page-info.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-front-page-info.txt	2019-05-22 15:20:07 +0000
@@ -17,7 +17,7 @@
 not use Launchpad for bug tracking.
 
     >>> anon_browser.open(
-    ...     'http://bugs.launchpad.dev/test-project/+bugs')
+    ...     'http://bugs.launchpad.test/test-project/+bugs')
     >>> uses_malone_p = find_tag_by_id(anon_browser.contents, 'no-malone')
     >>> print(extract_text(uses_malone_p))
     Test-project must be configured in order for Launchpad to forward bugs to
@@ -30,7 +30,7 @@
     True
 
     >>> admin_browser.open(
-    ...   'http://bugs.launchpad.dev/test-project/+bugs')
+    ...   'http://bugs.launchpad.test/test-project/+bugs')
     >>> enable_tracker = find_tag_by_id(
     ...     admin_browser.contents, 'no-malone-edit')
     >>> print(extract_text(enable_tracker))
@@ -46,7 +46,7 @@
     ...     official_malone=True)
     >>> logout()
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/uses-malone/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/uses-malone/+bugs')
     >>> bug_supervisor = find_tag_by_id(
     ...     anon_browser.contents, 'bug-supervisor')
     >>> print(extract_text(bug_supervisor))
@@ -67,7 +67,7 @@
     >>> test_project.bugtracker = some_tracker
     >>> logout()
     >>> anon_browser.open(
-    ...   'http://bugs.launchpad.dev/test-project/+bugs')
+    ...   'http://bugs.launchpad.test/test-project/+bugs')
     >>> tracker_text = find_tag_by_id(anon_browser.contents, 'bugtracker')
     >>> print(extract_text(tracker_text))
     Bugs are tracked in tracker.example.com/.
@@ -83,7 +83,7 @@
     ...     in_ubuntu=True)
     >>> logout()
     >>> anon_browser.open(
-    ...   'http://bugs.launchpad.dev/test-project/+bugs')
+    ...   'http://bugs.launchpad.test/test-project/+bugs')
     >>> print(extract_text(
     ...     find_tag_by_id(anon_browser.contents, 'also-in-ubuntu')))
     Ubuntu also tracks bugs for packages derived from this project:

=== modified file 'lib/lp/bugs/stories/bugs/xx-front-page-search.txt'
--- lib/lp/bugs/stories/bugs/xx-front-page-search.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-front-page-search.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 It's possible to search bug reports across all of Launchpad from the
 Bugs front page.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('Search Bug Reports') is not None
     True
 
@@ -21,7 +21,7 @@
 bug listing.
 
     >>> from lp.bugs.tests.bug import print_bugtasks
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('All projects').selected = True
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
     >>> anon_browser.getControl('Search Bug Reports').click()
@@ -42,7 +42,7 @@
 Even if a product is specified in the bug target widget, all bug reports
 will be searched.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('All projects').selected = True
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
     >>> anon_browser.getControl(name='field.scope.target').value = 'evolution'
@@ -53,7 +53,7 @@
 The same is of course true if the target widget contains a non-existant
 project name.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('All projects').selected = True
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
     >>> anon_browser.getControl(name='field.scope.target').value = 'invalid'
@@ -75,13 +75,13 @@
 the project's bug listing, and the search will be performed there. If no
 name is specified, an error message will be displayed.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = ''
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
     >>> anon_browser.getControl('Search Bug Reports').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/bugs?...'
+    'http://bugs.launchpad.test/bugs?...'
     >>> anon_browser.getControl(name='field.searchtext').value
     'test bug'
 
@@ -92,13 +92,13 @@
 An error message will be displayed also if the project isn't registered
 in the Launchpad.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'invalid'
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
     >>> anon_browser.getControl('Search Bug Reports').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/bugs?...&field.scope.target=invalid...'
+    'http://bugs.launchpad.test/bugs?...&field.scope.target=invalid...'
     >>> anon_browser.getControl(name='field.searchtext').value
     'test bug'
     >>> for message in find_tags_by_class(anon_browser.contents, 'message'):
@@ -111,14 +111,14 @@
 
     >>> find_link = anon_browser.getLink('Find')
     >>> find_link.url
-    'http://bugs.launchpad.dev/bugs...'
+    'http://bugs.launchpad.test/bugs...'
 
 'Project' in this context means either a product, distribution or a
 project group.
 
 === Searching a product ===
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'evolution'
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
@@ -126,14 +126,14 @@
     >>> print(anon_browser.title)
     Bugs : Evolution...
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/evolution/+bugs?field.searchtext=test+bug...'
+    'http://bugs.launchpad.test/evolution/+bugs?field.searchtext=test+bug...'
     >>> print_bugtasks(anon_browser.contents)
     7 A test bug Evolution
       Medium New
 
 === Searching a project ===
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'gnome'
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
@@ -141,14 +141,14 @@
     >>> print(anon_browser.title)
     Bugs : GNOME
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/gnome/+bugs?field.searchtext=test+bug...'
+    'http://bugs.launchpad.test/gnome/+bugs?field.searchtext=test+bug...'
     >>> print_bugtasks(anon_browser.contents)
     7 A test bug
       Evolution Medium New
 
 === Searching a distribution ===
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl('One project').selected = True
     >>> anon_browser.getControl(name='field.scope.target').value = 'ubuntu'
     >>> anon_browser.getControl(name='field.searchtext').value = 'test bug'
@@ -156,7 +156,7 @@
     >>> print(anon_browser.title)
     Bugs : Ubuntu...
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/ubuntu/+bugs?field.searchtext=test+bug...'
+    'http://bugs.launchpad.test/ubuntu/+bugs?field.searchtext=test+bug...'
     >>> print_bugtasks(anon_browser.contents)
     10 another test bug
        linux-source-2.6.15 (Ubuntu)  Medium  New
@@ -166,7 +166,7 @@
 Like with all other bug searches, it's possible to jump a bug by
 specifying only the bug id as the search term.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> anon_browser.getControl(name='field.searchtext').value = '7'
     >>> anon_browser.getControl('Search Bug Reports').click()
     >>> anon_browser.url

=== modified file 'lib/lp/bugs/stories/bugs/xx-front-page-statistics.txt'
--- lib/lp/bugs/stories/bugs/xx-front-page-statistics.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-front-page-statistics.txt	2019-05-22 15:20:07 +0000
@@ -7,7 +7,7 @@
 * number of external bug trackers registered
 * number of CVE bug links
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/')
+    >>> anon_browser.open('http://bugs.launchpad.test/')
     >>> statistics = find_portlet(
     ...     anon_browser.contents, 'Statistics')
     >>> print(extract_text(statistics))

=== modified file 'lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt'
--- lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt	2019-05-22 15:20:07 +0000
@@ -6,7 +6,7 @@
 No Privileges Person opens a bug and sets its status to 'Incomplete'.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/11/+editstatus')
+    ...     'http://bugs.launchpad.test/jokosher/+bug/11/+editstatus')
     >>> user_browser.getControl('Status').value = ['Incomplete']
     >>> user_browser.getControl('Save Changes').click()
     >>> print(extract_text(
@@ -17,7 +17,7 @@
 search form.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
+    ...     'http://bugs.launchpad.test/jokosher/+bugs?advanced=1')
 
 Bugs that have been marked 'Incomplete' but for which no new information
 was supplied (through comments) are 'Incomplete (without response)'.
@@ -26,13 +26,13 @@
     ...     ['INCOMPLETE_WITHOUT_RESPONSE'])
     >>> user_browser.getControl('Search', index=1).click()
     >>> find_tag_by_id(user_browser.contents, 'bugs-table-listing').findChild('a')
-    <a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">...</a>
+    <a href="http://bugs.launchpad.test/jokosher/+bug/11"; class="bugtitle">...</a>
 
 Bugs that have been marked incomplete and for which new information was
 supplied are 'Incomplete (with response)'.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
+    ...     'http://bugs.launchpad.test/jokosher/+bugs?advanced=1')
     >>> user_browser.getControl(name='field.status:list').value = (
     ...     ['INCOMPLETE_WITH_RESPONSE'])
     >>> user_browser.getControl('Search', index=1).click()
@@ -47,7 +47,7 @@
 No Privileges Person can supply new information by posting a new
 comment for the bug.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/jokosher/+bug/11')
+    >>> user_browser.open('http://bugs.launchpad.test/jokosher/+bug/11')
     >>> user_browser.getControl(name='field.comment').value = (
     ...     'More information here.')
     >>> user_browser.getControl('Post Comment').click()
@@ -58,23 +58,23 @@
 They try again to find that bug using the advanced search form.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
+    ...     'http://bugs.launchpad.test/jokosher/+bugs?advanced=1')
     >>> user_browser.getControl(name='field.status:list').value = (
     ...     ['INCOMPLETE_WITH_RESPONSE'])
     >>> user_browser.getControl('Search', index=1).click()
     >>> find_tag_by_id(user_browser.contents, 'bugs-table-listing').findChild('a')
-    <a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">...</a>
+    <a href="http://bugs.launchpad.test/jokosher/+bug/11"; class="bugtitle">...</a>
 
 The bug is there, since they supplied new information in a comment. No
 Privileges Person makes sure that it no longer is in the list of
 incomplete bugs without response.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
+    ...     'http://bugs.launchpad.test/jokosher/+bugs?advanced=1')
     >>> user_browser.getControl(name='field.status:list').value = (
     ...     ['INCOMPLETE_WITH_RESPONSE'])
     >>> user_browser.getControl('Search', index=1).click()
-    >>> ('<a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">' in
+    >>> ('<a href="http://bugs.launchpad.test/jokosher/+bug/11"; class="bugtitle">' in
     ...     find_tag_by_id(user_browser.contents, 'bugs-table-listing'))
     False
 
@@ -90,7 +90,7 @@
 Jokosher uses Launchpad to track bugs, so a notice is displayed
 stating that the bug report will be marked for expiration.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/jokosher/+bug/11')
+    >>> user_browser.open('http://bugs.launchpad.test/jokosher/+bug/11')
     >>> print(extract_text(
     ...     find_tag_by_id(user_browser.contents, 'can-expire')))
     This bug report will be marked for expiration in 59 days if no further
@@ -120,7 +120,7 @@
     >>> flush_database_updates()
     >>> logout()
 
-    >>> user_browser.open('http://bugs.launchpad.dev/jokosher/+bug/11')
+    >>> user_browser.open('http://bugs.launchpad.test/jokosher/+bug/11')
     >>> print(extract_text(
     ...     find_tag_by_id(user_browser.contents, 'can-expire')))
     This bug report was marked for expiration 1 days ago.
@@ -135,7 +135,7 @@
 bug page. To see the behaviour of the bug listing, we need another
 expirable bug. No Privileges Person marks another bug as Incomplete
 
-    >>> user_browser.open('http://bugs.launchpad.dev/jokosher/+bug/12')
+    >>> user_browser.open('http://bugs.launchpad.test/jokosher/+bug/12')
     >>> user_browser.getControl('Status').value = ['Incomplete']
     >>> user_browser.getControl('Save Changes', index=0).click()
 
@@ -228,7 +228,7 @@
     >>> logout()
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/8')
+    ...     'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/8')
     >>> user_browser.getControl('Status').value = ['Incomplete']
     >>> user_browser.getControl('Save Changes', index=0).click()
     >>> print(find_tag_by_id(user_browser.contents, 'can-expire'))
@@ -237,7 +237,7 @@
 If No Privileges Person hacks the URL to see a listing of Debian's
 expirable bugs they read that Debian does not use bug expiration.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/debian/+expirable-bugs')
+    >>> user_browser.open('http://bugs.launchpad.test/debian/+expirable-bugs')
     >>> print(extract_text(find_main_content(user_browser.contents).p))
     This project has not enabled bug expiration. No bugs can expire.
     Project administrator's may choose to enable bug expiration by
@@ -252,7 +252,7 @@
 We set bug #11 to Incomplete again.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bug/11/+editstatus')
+    ...     'http://bugs.launchpad.test/jokosher/+bug/11/+editstatus')
     >>> user_browser.getControl('Status').value = ['Incomplete']
     >>> user_browser.getControl('Save Changes').click()
 
@@ -261,20 +261,20 @@
 response) bugs.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
+    ...     'http://bugs.launchpad.test/jokosher/+bugs?advanced=1')
     >>> user_browser.getControl(name='field.status:list').value = (
     ...     ['INCOMPLETE_WITHOUT_RESPONSE'])
     >>> user_browser.getControl('Search', index=1).click()
-    >>> ('<a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">' in
+    >>> ('<a href="http://bugs.launchpad.test/jokosher/+bug/11"; class="bugtitle">' in
     ...     str(find_tag_by_id(user_browser.contents, 'bugs-table-listing')))
     True
 
 A default search turns that bug up as well.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/jokosher')
+    >>> user_browser.open('http://bugs.launchpad.test/jokosher')
     >>> user_browser.getControl('Search', index=0).click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/jokosher/+bugs?...&field.status%3Alist=INCOMPLETE_WITH_RESPONSE&field.status%3Alist=INCOMPLETE_WITHOUT_RESPONSE...
-    >>> ('<a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">' in
+    http://bugs.launchpad.test/jokosher/+bugs?...&field.status%3Alist=INCOMPLETE_WITH_RESPONSE&field.status%3Alist=INCOMPLETE_WITHOUT_RESPONSE...
+    >>> ('<a href="http://bugs.launchpad.test/jokosher/+bug/11"; class="bugtitle">' in
     ...     str(find_tag_by_id(user_browser.contents, 'bugs-table-listing')))
     True

=== modified file 'lib/lp/bugs/stories/bugs/xx-link-bug-to-branch.txt'
--- lib/lp/bugs/stories/bugs/xx-link-bug-to-branch.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-link-bug-to-branch.txt	2019-05-22 15:20:07 +0000
@@ -5,10 +5,10 @@
 link a branch to a bug.
 
     >>> user_browser.open(
-    ...     "http://bugs.launchpad.dev/firefox/+bug/1";)
+    ...     "http://bugs.launchpad.test/firefox/+bug/1";)
     >>> user_browser.getLink('Link a related branch').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1/+addbranch
+    http://bugs.launchpad.test/firefox/+bug/1/+addbranch
     >>> print(extract_text(find_tag_by_id(
     ...    user_browser.contents, 'maincontent')))
     Add a branch to bug #1...
@@ -27,7 +27,7 @@
 a related branch.
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/firefox/+bug/1
+    http://bugs.launchpad.test/firefox/+bug/1
     >>> print(extract_text(user_browser.contents))
     Bug #1...
     Successfully registered branch main for this bug.
@@ -39,7 +39,7 @@
 We can delete existing links between a bug and a branch.
 
     >>> delete_branch_link_url = (
-    ...     'http://code.launchpad.dev/~name12/firefox/main/+bug/1/+delete')
+    ...     'http://code.launchpad.test/~name12/firefox/main/+bug/1/+delete')
     >>> link = user_browser.getLink(url=delete_branch_link_url)
     >>> link.click()
     >>> print(extract_text(find_tag_by_id(

=== modified file 'lib/lp/bugs/stories/bugs/xx-malone-homepage.txt'
--- lib/lp/bugs/stories/bugs/xx-malone-homepage.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-malone-homepage.txt	2019-05-22 15:20:07 +0000
@@ -1,8 +1,8 @@
 Say hello to Bugs. :)
 
-    >>> browser.open('http://bugs.launchpad.dev/')
+    >>> browser.open('http://bugs.launchpad.test/')
     >>> browser.url
-    'http://bugs.launchpad.dev/'
+    'http://bugs.launchpad.test/'
 
     >>> print(browser.title)
     Launchpad Bugs
@@ -13,6 +13,6 @@
     >>> for link in related_pages.findAll('a'):
     ...     print("%s\n  --> %s" % (extract_text(link), link.get('href')))
     Bug trackers
-    --> http://bugs.launchpad.dev/bugs/bugtrackers
+    --> http://bugs.launchpad.test/bugs/bugtrackers
     CVE tracker
-    --> http://bugs.launchpad.dev/bugs/cve
+    --> http://bugs.launchpad.test/bugs/cve

=== modified file 'lib/lp/bugs/stories/bugs/xx-numbered-comments.txt'
--- lib/lp/bugs/stories/bugs/xx-numbered-comments.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-numbered-comments.txt	2019-05-22 15:20:07 +0000
@@ -6,7 +6,7 @@
 the permalink URL. To help users find the relevant comments more
 easily, the numbers are displayed in the comment header.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/jokosher/+bug/11')
+    >>> anon_browser.open('http://bugs.launchpad.test/jokosher/+bug/11')
     >>> comments = find_tags_by_class(
     ...     anon_browser.contents, 'boardComment')
     >>> for comment in comments:

=== modified file 'lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt'
--- lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt	2019-05-22 15:20:07 +0000
@@ -9,7 +9,7 @@
 If there are no milestones with bugs, then there is no milestone-targeted
 portlet.
 
-    >>> anon_browser.open("http://bugs.launchpad.dev/firefox";)
+    >>> anon_browser.open("http://bugs.launchpad.test/firefox";)
     >>> portlet = find_portlet(
     ...     anon_browser.contents, "Milestone-targeted bugs")
     >>> print(portlet)
@@ -41,7 +41,7 @@
 Now Firefox has a milestone-targeted bugs portlet on the project's bugs home
 page.
 
-    >>> anon_browser.open("http://bugs.launchpad.dev/firefox";)
+    >>> anon_browser.open("http://bugs.launchpad.test/firefox";)
     >>> portlet = find_portlet(
     ...     anon_browser.contents, "Milestone-targeted bugs")
     >>> print(extract_text(portlet))
@@ -58,7 +58,7 @@
 
 And look at the portlet.
 
-    >>> anon_browser.open("http://bugs.launchpad.dev/debian/sarge/+bugs";)
+    >>> anon_browser.open("http://bugs.launchpad.test/debian/sarge/+bugs";)
     >>> portlet = find_portlet(
     ...     anon_browser.contents, "Milestone-targeted bugs")
     >>> print(extract_text(portlet))

=== modified file 'lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt'
--- lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 This portlet is not available from a distribution's bug page if it
 does not use Launchpad for tracking bugs.
 
-  >>> anon_browser.open("http://bugs.launchpad.dev/debian/+bugs";)
+  >>> anon_browser.open("http://bugs.launchpad.test/debian/+bugs";)
   >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
   >>> print(portlet)
   None
@@ -14,25 +14,25 @@
   >>> from lp.testing.service_usage_helpers import set_service_usage
   >>> set_service_usage('debian', bug_tracking_usage='LAUNCHPAD')
 
-  >>> anon_browser.open("http://bugs.launchpad.dev/debian/+bugs";)
-  >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
-  >>> print(extract_text(portlet))
-  Series-targeted bugs
-  1
-  sarge
-  2
-  woody
-
-  >>> anon_browser.open("http://bugs.launchpad.dev/debian/sarge/+bugs";)
-  >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
-  >>> print(extract_text(portlet))
-  Series-targeted bugs
-  1
-  sarge
-  2
-  woody
-
-  >>> anon_browser.open("http://bugs.launchpad.dev/ubuntu/+bugs";)
+  >>> anon_browser.open("http://bugs.launchpad.test/debian/+bugs";)
+  >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
+  >>> print(extract_text(portlet))
+  Series-targeted bugs
+  1
+  sarge
+  2
+  woody
+
+  >>> anon_browser.open("http://bugs.launchpad.test/debian/sarge/+bugs";)
+  >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
+  >>> print(extract_text(portlet))
+  Series-targeted bugs
+  1
+  sarge
+  2
+  woody
+
+  >>> anon_browser.open("http://bugs.launchpad.test/ubuntu/+bugs";)
   >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
   >>> print(extract_text(portlet))
   Series-targeted bugs
@@ -42,19 +42,19 @@
   warty
 
   >>> print(anon_browser.getLink("hoary").url)
-  http://bugs.launchpad.dev/ubuntu/hoary/+bugs
+  http://bugs.launchpad.test/ubuntu/hoary/+bugs
 
 The same portlet is also available for project and project series
 listings and homepages:
 
-  >>> anon_browser.open("http://bugs.launchpad.dev/firefox/+bugs";)
+  >>> anon_browser.open("http://bugs.launchpad.test/firefox/+bugs";)
   >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
   >>> print(extract_text(portlet))
   Series-targeted bugs
   1
   1.0
 
-  >>> anon_browser.open("http://bugs.launchpad.dev/firefox/1.0/+bugs";)
+  >>> anon_browser.open("http://bugs.launchpad.test/firefox/1.0/+bugs";)
   >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
   >>> print(extract_text(portlet))
   Series-targeted bugs

=== modified file 'lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt	2019-05-22 15:20:07 +0000
@@ -4,7 +4,7 @@
 The +bugs-index page for a product on the bugs domain presents some basic
 information the bugs in it. It also displays the list of bugs.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox/+bugs')
     >>> anon_browser.title
     'Bugs : Mozilla Firefox'
     >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
@@ -14,27 +14,27 @@
 
     >>> anon_browser.getLink('Open bugs').click()
     >>> anon_browser.url
-    'http://bugs.launchpad.dev/firefox/+bugs'
+    'http://bugs.launchpad.test/firefox/+bugs'
     >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
     True
 
 It also has a link to subscribe to bug mail (which is implemented in
 JavaScript so it doesn't actually go anywhere).
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bugs')
+    >>> user_browser.open('http://bugs.launchpad.test/firefox/+bugs')
     >>> user_browser.getLink('Subscribe to bug mail').click()
     >>> user_browser.url
-    'http://bugs.launchpad.dev/firefox/+subscribe'
+    'http://bugs.launchpad.test/firefox/+subscribe'
 
 CVE Bugs
 --------
 
 It also displays the number of open bugs associated with a CVE.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/evolution/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/evolution/+bugs')
     >>> cve_bugs_link = anon_browser.getLink('Open CVE bugs')
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bugs')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox/+bugs')
     >>> cve_bugs_link = anon_browser.getLink('Open CVE bugs')
 
 The link takes you to the list of bugs with CVEs linked to them.
@@ -74,7 +74,7 @@
 can expire when the project has enabled bug expiration. Jokosher
 is such a project.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/jokosher')
+    >>> anon_browser.open('http://bugs.launchpad.test/jokosher')
     >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
 
 The link goes to the expirable bugs page, where the anonymous user can
@@ -87,7 +87,7 @@
 Product series may also have a link to expirable bugs. Jokosher's trunk
 series has the link because Jokosher has enabled bug expiration.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/jokosher/trunk')
+    >>> anon_browser.open('http://bugs.launchpad.test/jokosher/trunk')
     >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
     >>> expirable_bugs_link.click()
     >>> print(anon_browser.title)
@@ -96,7 +96,7 @@
 Thunderbird has not enabled bug expiration; the anonymous user
 cannot see any link reporting that bugs can expire.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/thunderbird')
+    >>> anon_browser.open('http://bugs.launchpad.test/thunderbird')
     >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
     Traceback (most recent call last):
     ...
@@ -109,7 +109,7 @@
 There's also portlets for easy searching by tags and filters. Its content is
 loaded using Javascript in a separate request.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox')
     >>> tags_portlet = find_tag_by_id(anon_browser.contents, 'portlet-tags')
     >>> anon_browser.getLink(id='tags-content-link').click()
     >>> print(extract_text(anon_browser.contents))
@@ -128,7 +128,7 @@
     4  Reflow problems with complex page layouts Mozilla Firefox
        Medium New
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/firefox')
+    >>> anon_browser.open('http://bugs.launchpad.test/firefox')
     >>> anon_browser.getLink('Critical bugs').click()
     >>> print_bugtasks(anon_browser.contents)
     5 Firefox install instructions should be complete Mozilla Firefox

=== modified file 'lib/lp/bugs/stories/bugs/xx-project-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-project-bugs-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-project-bugs-page.txt	2019-05-22 15:20:07 +0000
@@ -3,6 +3,6 @@
 The default page for a ProjectGroup on the bugs domain shows the bug listing
 for the ProjectGroup.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/gnome')
+    >>> anon_browser.open('http://bugs.launchpad.test/gnome')
     >>> print(anon_browser.title)
     Bugs : GNOME

=== modified file 'lib/lp/bugs/stories/bugs/xx-remote-bug-comments.txt'
--- lib/lp/bugs/stories/bugs/xx-remote-bug-comments.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-remote-bug-comments.txt	2019-05-22 15:20:07 +0000
@@ -5,7 +5,7 @@
 formatted for display in a way that distinguishes them from comments
 entered within Launchpad itself.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/redfish/+bug/15')
+    >>> user_browser.open('http://bugs.launchpad.test/redfish/+bug/15')
     >>> remote_bug_comment = find_tags_by_class(
     ...     user_browser.contents, 'remoteBugComment', only_first=True)
     >>> details = remote_bug_comment.find(
@@ -39,7 +39,7 @@
 
     >>> user_browser.getLink('Reply on Debian Bug tracker').click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/redfish/+bug/15/comments/1
+    http://bugs.launchpad.test/redfish/+bug/15/comments/1
 
 We enter a comment, and submit the form.
 
@@ -87,7 +87,7 @@
     >>> flush_database_updates()
     >>> logout()
 
-    >>> user_browser.open('http://bugs.launchpad.dev/redfish/+bug/15')
+    >>> user_browser.open('http://bugs.launchpad.test/redfish/+bug/15')
     >>> last_bug_comment = find_tags_by_class(
     ...     user_browser.contents, 'remoteBugComment')[-1]
     >>> print(extract_text(last_bug_comment))
@@ -104,7 +104,7 @@
 When an anonymous user views a remote comment, the reply links are
 hidden, since they can't be used anonymously anyway.
 
-    >>> anon_browser.open('http://bugs.launchpad.dev/redfish/+bug/15')
+    >>> anon_browser.open('http://bugs.launchpad.test/redfish/+bug/15')
     >>> remote_bug_comment = find_tags_by_class(
     ...     anon_browser.contents, 'remoteBugComment', only_first=True)
     >>> activity = remote_bug_comment.find(

=== modified file 'lib/lp/bugs/stories/bugs/xx-switch-to-malone.txt'
--- lib/lp/bugs/stories/bugs/xx-switch-to-malone.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-switch-to-malone.txt	2019-05-22 15:20:07 +0000
@@ -7,12 +7,12 @@
 When project doesn't use Launchpad as its official bugtracker, it's
 possible for its bugs to have an Unknown importance.
 
-    >>> admin_browser.open('http://launchpad.dev/debian/+edit')
+    >>> admin_browser.open('http://launchpad.test/debian/+edit')
     >>> admin_browser.getControl(
     ...     'Bugs in this project are tracked in Launchpad').selected
     False
     >>> user_browser.open(
-    ...     'http://launchpad.dev/debian'
+    ...     'http://launchpad.test/debian'
     ...     '/+source/evolution/+bug/7/+editstatus')
     >>> main = find_main_content(user_browser.contents)
     >>> read_only_icon = main.find('span', {'class': 'sprite read-only'})
@@ -22,7 +22,7 @@
 If the project switches to use Launchpad as its bug tracker, the
 importance will still have an Unknown value.
 
-    >>> admin_browser.open('http://launchpad.dev/debian/+edit')
+    >>> admin_browser.open('http://launchpad.test/debian/+edit')
     >>> admin_browser.getControl(
     ...     'Bugs in this project are tracked in Launchpad').selected = True
     >>> admin_browser.getControl('Change', index=3).click()
@@ -30,7 +30,7 @@
     'Debian in Launchpad'
 
     >>> user_browser.open(
-    ...     'http://launchpad.dev'
+    ...     'http://launchpad.test'
     ...     '/debian/+source/evolution/+bug/7/+editstatus')
     >>> main = find_main_content(user_browser.contents)
     >>> read_only_icon = main.find('span', {'class': 'sprite read-only'})

=== modified file 'lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt'
--- lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 
 For example, bug one has more than one Package field.
 
-    >>> user_browser.open('http://launchpad.dev/bugs/1')
+    >>> user_browser.open('http://launchpad.test/bugs/1')
     >>> user_browser.getControl('Package')
     Traceback (most recent call last):
     ...

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 
 It is possible to set the bug importance using the bugtask edit form.
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/bugs/10')
+    >>> admin_browser.open('http://bugs.launchpad.test/bugs/10')
     >>> importance_control = admin_browser.getControl('Importance')
     >>> print('\n'.join(importance_control.displayOptions))
     Undecided
@@ -57,7 +57,7 @@
     >>> firefox.bug_supervisor = no_priv
     >>> user_sees_importance_widget(
     ...     user=no_priv,
-    ...     url="http://bugs.launchpad.dev/firefox/+bug/1/+editstatus";)
+    ...     url="http://bugs.launchpad.test/firefox/+bug/1/+editstatus";)
     True
 
 For a product owner.
@@ -70,7 +70,7 @@
     >>> sample_person = personset.getByName("name12")
     >>> user_sees_importance_widget(
     ...     user=sample_person,
-    ...     url="http://bugs.launchpad.dev/firefox/+bug/1/+editstatus";)
+    ...     url="http://bugs.launchpad.test/firefox/+bug/1/+editstatus";)
     True
 
 For someone else. We'll unset no_priv as the bug supervisor, and note that
@@ -80,7 +80,7 @@
     >>> firefox.bug_supervisor = None
     >>> user_sees_importance_widget(
     ...     user=no_priv,
-    ...     url="http://bugs.launchpad.dev/firefox/+bug/1/+editstatus";)
+    ...     url="http://bugs.launchpad.test/firefox/+bug/1/+editstatus";)
     False
 
 For a distribution bug supervisor.
@@ -89,7 +89,7 @@
     >>> ubuntu.bug_supervisor = no_priv
     >>> user_sees_importance_widget(
     ...     user=no_priv,
-    ...     url='http://bugs.launchpad.dev/'
+    ...     url='http://bugs.launchpad.test/'
     ...         'ubuntu/+source/mozilla-firefox/+bug/1/+editstatus')
     True
 
@@ -101,7 +101,7 @@
 
     >>> user_sees_importance_widget(
     ...     user=mark,
-    ...     url='http://bugs.launchpad.dev/'
+    ...     url='http://bugs.launchpad.test/'
     ...         'ubuntu/+source/mozilla-firefox/+bug/1/+editstatus')
     True
 
@@ -111,6 +111,6 @@
     >>> ubuntu.bug_supervisor = None
     >>> user_sees_importance_widget(
     ...     user=no_priv,
-    ...     url='http://bugs.launchpad.dev/'
+    ...     url='http://bugs.launchpad.test/'
     ...         'ubuntu/+source/mozilla-firefox/+bug/1/+editstatus')
     False

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt	2019-05-22 15:20:07 +0000
@@ -10,7 +10,7 @@
 == Unprivileged users ==
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/'
     ...     'mozilla-firefox/+bug/1/+editstatus')
 
     >>> status_control = user_browser.getControl('Status')
@@ -23,7 +23,7 @@
     >>> user_browser.getControl('Save Changes').click()
 
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1
     >>> print_highlighted_bugtask(user_browser)
     mozilla-firefox (Ubuntu) ... Confirmed  Medium Unassigned ...
 
@@ -31,7 +31,7 @@
 those statuses are not shown in the UI:
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/'
     ...     'mozilla-firefox/+bug/1/+editstatus')
 
     >>> status_control = user_browser.getControl('Status')
@@ -52,7 +52,7 @@
 
 Ubuntu needs a Bug Supervisor first of all:
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/ubuntu/+bugsupervisor')
+    >>> admin_browser.open('http://bugs.launchpad.test/ubuntu/+bugsupervisor')
     >>> admin_browser.getControl('Bug Supervisor').value = (
     ...     'test@xxxxxxxxxxxxx')
     >>> admin_browser.getControl('Change').click()
@@ -68,7 +68,7 @@
     ...     auth='Basic test@xxxxxxxxxxxxx:test')
 
     >>> bug_supervisor_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/'
     ...     'mozilla-firefox/+bug/1/+editstatus')
 
     >>> status_control = bug_supervisor_browser.getControl('Status')
@@ -79,7 +79,7 @@
     >>> bug_supervisor_browser.getControl('Save Changes').click()
 
     >>> print(bug_supervisor_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1
     >>> print_highlighted_bugtask(bug_supervisor_browser)
     mozilla-firefox (Ubuntu) ... Won't Fix  Medium Unassigned ...
 
@@ -87,7 +87,7 @@
 status. Earlier it was not even displayed as an option.
 
     >>> user_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/'
     ...     'mozilla-firefox/+bug/1/+editstatus')
 
     >>> status_control = user_browser.getControl('Status')
@@ -105,14 +105,14 @@
     >>> user_browser.getControl('Save Changes').click()
 
     >>> print(bug_supervisor_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+bug/1
     >>> print_highlighted_bugtask(bug_supervisor_browser)
     mozilla-firefox (Ubuntu) ... Won't Fix  Medium Unassigned ...
 
 The Bug Supervisor for Ubuntu can also change the status to Triaged:
 
     >>> bug_supervisor_browser.open(
-    ...     'http://bugs.launchpad.dev/ubuntu/+source/'
+    ...     'http://bugs.launchpad.test/ubuntu/+source/'
     ...     'iceweasel/+bug/1/+editstatus')
 
     >>> status_control = bug_supervisor_browser.getControl('Status')
@@ -123,6 +123,6 @@
     >>> bug_supervisor_browser.getControl('Save Changes').click()
 
     >>> print(bug_supervisor_browser.url)
-    http://bugs.launchpad.dev/ubuntu/+source/iceweasel/+bug/1
+    http://bugs.launchpad.test/ubuntu/+source/iceweasel/+bug/1
     >>> print_highlighted_bugtask(bug_supervisor_browser)
     iceweasel (Ubuntu) ... Triaged  Medium Unassigned ...

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt	2019-05-22 15:20:07 +0000
@@ -11,7 +11,7 @@
 of the bug task table lead them to the bug task edit forms of the
 respective bug task.
 
-    >>> admin_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
+    >>> admin_browser.open('http://bugs.launchpad.test/firefox/+bug/1')
     >>> print(extract_text(
     ...     find_tag_by_id(admin_browser.contents, 'affected-software')))
     Affects                     Status    Importance   Assigned to...
@@ -21,9 +21,9 @@
     ...
 
     >>> print(admin_browser.getLink('New', index=0).url)
-    http://bugs.launchpad.dev/firefox/+bug/1/+editstatus
+    http://bugs.launchpad.test/firefox/+bug/1/+editstatus
     >>> print(admin_browser.getLink('Low', index=0).url)
-    http://bugs.launchpad.dev/firefox/+bug/1/+editstatus
+    http://bugs.launchpad.test/firefox/+bug/1/+editstatus
 
     >>> print(admin_browser.getLink('Confirmed').url)
     Traceback (most recent call last):
@@ -31,11 +31,11 @@
     LinkNotFoundError
 
     >>> print(admin_browser.getLink('Low', index=1).url)
-    http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/1/+editstatus
+    http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/1/+editstatus
     >>> print(admin_browser.getLink('New', index=1).url)
-    http://bugs...dev/ubuntu/+source/mozilla-firefox/+bug/1/+editstatus
+    http://bugs...test/ubuntu/+source/mozilla-firefox/+bug/1/+editstatus
     >>> print(admin_browser.getLink('Medium').url)
-    http://bugs...dev/ubuntu/+source/mozilla-firefox/+bug/1/+editstatus
+    http://bugs...test/ubuntu/+source/mozilla-firefox/+bug/1/+editstatus
 
     >>> admin_browser.getLink('New').click()
     >>> print(extract_text(admin_browser.contents))

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 
 A bug is unassigned by choosing the "Assigned to" -> "Nobody" option.
 
-    >>> admin_browser.open("http://launchpad.dev/firefox/+bug/1";)
+    >>> admin_browser.open("http://launchpad.test/firefox/+bug/1";)
 
 Bug 1 is currently assigned to someone.
 
@@ -31,7 +31,7 @@
 warned immediately after the assignment, so that they can change their
 choice if it was mistaken.
 
-    >>> admin_browser.open("http://launchpad.dev/firefox/+bug/1";)
+    >>> admin_browser.open("http://launchpad.test/firefox/+bug/1";)
     >>> assignee_control = admin_browser.getControl(
     ...     name="firefox.assignee.option", index=0)
     >>> assignee_control.value = ["firefox.assignee.assign_to"]
@@ -50,7 +50,7 @@
 If the new assignee does have bugs assigned, but not in the relevant pillar,
 the user will be warned too.
 
-    >>> admin_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
+    >>> admin_browser.open("http://bugs.launchpad.test/jokosher/+bug/11";)
     >>> assignee_control = admin_browser.getControl(
     ...     name="jokosher.assignee.option", index=0)
     >>> assignee_control.value = ["jokosher.assignee.assign_to"]
@@ -77,13 +77,13 @@
 
     >>> logout()
 
-    >>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
+    >>> user_browser.open("http://bugs.launchpad.test/jokosher/+bug/11";)
     >>> assignee_control = user_browser.getControl(
     ...     name="jokosher.assignee.option", index=0)
     >>> assignee_control.value = ["jokosher.assignee.assign_to_me"]
     >>> user_browser.getControl("Save Changes", index=0).click()
     >>> print(user_browser.url)
-    http://bugs.launchpad.dev/jokosher/+bug/11
+    http://bugs.launchpad.test/jokosher/+bug/11
     >>> print(first_tag_by_class(
     ...     user_browser.contents, 'warning message'))
     None
@@ -113,7 +113,7 @@
     0
 
     >>> logout()
-    >>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
+    >>> user_browser.open("http://bugs.launchpad.test/jokosher/+bug/11";)
     >>> assignee_control = user_browser.getControl(
     ...     name="jokosher.assignee.option", index=0)
     >>> assignee_control.value = ["jokosher.assignee.assign_to"]
@@ -131,7 +131,7 @@
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> no_privs_team_name = factory.makeTeam(owner=no_priv).name
     >>> logout()
-    >>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
+    >>> user_browser.open("http://bugs.launchpad.test/jokosher/+bug/11";)
     >>> assignee_control = user_browser.getControl(
     ...     name="jokosher.assignee.option", index=0)
     >>> assignee_control.value = ["jokosher.assignee.assign_to"]
@@ -143,7 +143,7 @@
 
 But if they try to set other persons or teams, they get an error message.
 
-    >>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
+    >>> user_browser.open("http://bugs.launchpad.test/jokosher/+bug/11";)
     >>> assignee_control = user_browser.getControl(
     ...     name="jokosher.assignee.option", index=0)
     >>> assignee_control.value = ["jokosher.assignee.assign_to"]

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt	2019-05-22 15:20:07 +0000
@@ -8,7 +8,7 @@
 when bugs should be fixed by setting up some milestones.
 
     >>> owner_browser = setupBrowser('Basic test@xxxxxxxxxxxxx:test')
-    >>> owner_browser.open('http://launchpad.dev/firefox/+bug/1')
+    >>> owner_browser.open('http://launchpad.test/firefox/+bug/1')
 
 Bug 1 affects the Firefox project. The 1.0 milestone is available for
 use, but the bug is not yet targeted to that milestone.
@@ -43,7 +43,7 @@
 
 Sample Person defines milestone 1.0.1 for firefox series 1.0...
 
-    >>> owner_browser.open('http://launchpad.dev/firefox/1.0/+addmilestone')
+    >>> owner_browser.open('http://launchpad.test/firefox/1.0/+addmilestone')
     >>> name_field = owner_browser.getControl('Name:')
     >>> name_field.value = '1.0.1'
     >>> owner_browser.getControl('Register Milestone').click()
@@ -51,7 +51,7 @@
 
 ...and then they approve the nomination of bug #1 for the firefox series 1.0.
 
-    >>> owner_browser.open('http://launchpad.dev/firefox/+bug/1')
+    >>> owner_browser.open('http://launchpad.test/firefox/+bug/1')
     >>> owner_browser.getControl('Approve', index=0).click()
 
 Sample Person assigns the firefox series 1.0 bugtask for bug 1 to
@@ -75,14 +75,14 @@
 distribution. They decide to assign some bug to some milestones.
 First they register a new milestone.
 
-    >>> admin_browser.open('http://launchpad.dev/ubuntu/hoary/+addmilestone')
+    >>> admin_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
     >>> name_field = admin_browser.getControl('Name:')
     >>> name_field.value = '5.04.rc1'
     >>> admin_browser.getControl('Register Milestone').click()
 
 They target bug #1 to milestone 5.04-rc1 for Ubuntu.
 
-    >>> admin_browser.open('http://launchpad.dev/firefox/+bug/1')
+    >>> admin_browser.open('http://launchpad.test/firefox/+bug/1')
     >>> table = find_tag_by_id(admin_browser.contents, 'affected-software')
     >>> print(extract_text(table))
     Affects                      Status  Importance  Assigned to           Milestone

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt	2019-05-22 15:20:07 +0000
@@ -43,7 +43,7 @@
 To prepare, we must add a task that references a bug tracked
 elsewhere:
 
-    >>> user_browser.open('http://bugs.launchpad.dev/'
+    >>> user_browser.open('http://bugs.launchpad.test/'
     ...                   'jokosher/+bug/12/+choose-affected-product')
     >>> user_browser.getControl('Project').value = u'gnome-terminal'
     >>> user_browser.getControl('Continue').click()
@@ -55,7 +55,7 @@
     ...     'Register Bug Tracker and Add to Bug Report').click()
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/gnome-terminal/+bug/12'
+    'http://bugs.launchpad.test/gnome-terminal/+bug/12'
 
 The product owner cannot see the Status or Importance widgets:
 
@@ -67,7 +67,7 @@
     u'name12'
     >>> print_widget_visibility(
     ...     user=gnome_terminal.owner,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'gnome-terminal/+bug/12/+editstatus'))
     Status: False
     Importance: False
@@ -78,7 +78,7 @@
     >>> no_priv = getUtility(IPersonSet).getByName("no-priv")
     >>> print_widget_visibility(
     ...     user=no_priv,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'gnome-terminal/+bug/12/+editstatus'))
     Status: False
     Importance: False
@@ -89,7 +89,7 @@
     >>> gnome_terminal.bug_supervisor = no_priv
     >>> print_widget_visibility(
     ...     user=no_priv,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'gnome-terminal/+bug/12/+editstatus'))
     Status: False
     Importance: False
@@ -103,7 +103,7 @@
 To prepare, we add a task that references a bug that's tracked by
 email:
 
-    >>> user_browser.open('http://bugs.launchpad.dev/'
+    >>> user_browser.open('http://bugs.launchpad.test/'
     ...                   'gnome-terminal/+bug/12/+choose-affected-product')
     >>> user_browser.getControl('Project').value = u'alsa-utils'
     >>> user_browser.getControl('Continue').click()
@@ -114,7 +114,7 @@
     >>> user_browser.getControl('Add to Bug Report').click()
 
     >>> user_browser.url
-    'http://bugs.launchpad.dev/alsa-utils/+bug/12'
+    'http://bugs.launchpad.test/alsa-utils/+bug/12'
 
 The owner can see the Status and Importance widgets.
 
@@ -127,7 +127,7 @@
 
     >>> print_widget_visibility(
     ...     user=alsa_utils.owner,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'alsa-utils/+bug/12/+editstatus'))
     Status: True
     Importance: True
@@ -141,7 +141,7 @@
 
     >>> print_widget_visibility(
     ...     user=no_priv,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'alsa-utils/+bug/12/+editstatus'))
     Status: True
     Importance: False
@@ -152,7 +152,7 @@
     >>> alsa_utils.bug_supervisor = no_priv
     >>> print_widget_visibility(
     ...     user=no_priv,
-    ...     url=('http://bugs.launchpad.dev/'
+    ...     url=('http://bugs.launchpad.test/'
     ...          'alsa-utils/+bug/12/+editstatus'))
     Status: True
     Importance: True

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-subscribe-while-editing.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-subscribe-while-editing.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-subscribe-while-editing.txt	2019-05-22 15:20:07 +0000
@@ -3,7 +3,7 @@
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open(
-    ...     "http://launchpad.dev/firefox/+bug/5/+editstatus";)
+    ...     "http://launchpad.test/firefox/+bug/5/+editstatus";)
 
     >>> browser.getControl("Status").value = ["Confirmed"]
     >>> browser.getControl("Comment on this change (optional)").value = "test"
@@ -19,7 +19,7 @@
 If you're already subscribed, the checkbox is not shown.
 
     >>> browser.open(
-    ...     "http://launchpad.dev/firefox/+bug/5/+editstatus";)
+    ...     "http://launchpad.test/firefox/+bug/5/+editstatus";)
 
     >>> browser.getControl("Email me about changes to this bug report")
     Traceback (most recent call last):

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-view-editable-bug-task.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-view-editable-bug-task.txt	2018-06-30 16:09:44 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-view-editable-bug-task.txt	2019-05-22 15:20:07 +0000
@@ -1,7 +1,7 @@
 An anonymous user who tries to access the bugtask edit page will be
 redirected to the login page.
 
-    >>> browser.open("http://launchpad.dev/thunderbird/+bug/9/+editstatus";)
+    >>> browser.open("http://launchpad.test/thunderbird/+bug/9/+editstatus";)
     Traceback (most recent call last):
       ...
     Unauthorized: ...
@@ -22,7 +22,7 @@
 
     >>> logout()
 
-    >>> browser.open("http://launchpad.dev/firefox/+bug/1/+editstatus";)
+    >>> browser.open("http://launchpad.test/firefox/+bug/1/+editstatus";)
     Traceback (most recent call last):
       ...
     Unauthorized: ...
@@ -30,6 +30,6 @@
 Any logged-in user can edit a bugtask.
 
     >>> browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
-    >>> browser.open("http://laun

Follow ups