diff --git a/add-on/lib/domainmatcher.js b/add-on/lib/domainmatcher.js
index 59b0f23..91a9fc3 100644
--- a/add-on/lib/domainmatcher.js
+++ b/add-on/lib/domainmatcher.js
@@ -58,6 +58,14 @@
             return next;
         }
 
+        // Also apply a slightly looser match, to make rules in the form
+        // of *.example.com match example.com.
+        if (next.startsWith("*.") &&
+                next.endsWith(hostName) &&
+                next.length === hostName.length + 2) {
+            return next;
+        }
+
         return prev;
     };
 
diff --git a/add-on/lib/init.js b/add-on/lib/init.js
index fec0fc5..98cef0f 100644
--- a/add-on/lib/init.js
+++ b/add-on/lib/init.js
@@ -2,6 +2,17 @@
 // the "namespace" we'll use for all the content scripts in the extension.
 (function () {
     "use strict";
+
+    // If this code is being included by a unit test, the window object
+    // won't exist, so stub it out here.
+    try {
+        if (window === undefined) {
+            // This will throw in node...
+        }
+    } catch (e) {
+        global.window = {};
+    }
+
     window.WEB_API_MANAGER = {
         constants: {
             // The name of the cookie that will be used to push domain
diff --git a/package.json b/package.json
index ef82442..92f4a7e 100644
--- a/package.json
+++ b/package.json
@@ -26,9 +26,9 @@
         "firefox": "web-ext -s add-on run",
         "lint": "node_modules/eslint/bin/eslint.js .",
         "lint:fix": "node_modules/eslint/bin/eslint.js --fix .",
-        "test": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && cross-env node_modules/mocha/bin/mocha test/functional/*.js --only-local-tests",
+        "test": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && cross-env node_modules/mocha/bin/mocha test/unit/*.js test/functional/*.js --only-local-tests",
         "test:watch": "cross-env npm test --watch",
-        "test:all": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && cross-env node_modules/mocha/bin/mocha test/functional/*.js"
+        "test:all": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && cross-env node_modules/mocha/bin/mocha test/unit/*.js test/functional/*.js"
     },
     "pre-push": {
         "run": [
diff --git a/test/unit/rule-matching.js b/test/unit/rule-matching.js
new file mode 100644
index 0000000..e025d51
--- /dev/null
+++ b/test/unit/rule-matching.js
@@ -0,0 +1,75 @@
+/**
+ * Tests to ensure that the pattern matching code (what determins which
+ * standard blocking rules should be applied to which domain) is correct.
+ * 
+ * The code being tested here mostly lives in (from the project root)
+ * add-on/lib/domainmatcher.js
+ */
+"use strict";
+
+const assert = require("assert");
+const path = require("path");
+const addonLibPath = path.join(__dirname, "..", "..", "add-on", "lib");
+
+// These will end up not returing anything, but will instead populate
+// window.WEB_API_MANAGER
+const webAPIInitStub = require(path.join(addonLibPath, "init.js"));
+const domainMatcherStub = require(path.join(addonLibPath, "domainmatcher.js"));
+const domainMatcherLib = window.WEB_API_MANAGER.domainMatcherLib;
+
+describe("Host Pattern Matching", function () {
+
+    const testPatterns = [
+        "*.example.com",
+        "www.uic.edu",
+        "cs.uic.edu",
+    ];
+
+    describe("Exact matches", function () {
+
+        it("Positive case: input 'www.uic.edu' gives pattern 'www.uic.edu'", function (done) {
+
+            const testHostName = "www.uic.edu";
+            const matchingPattern = domainMatcherLib.matchHostName(testPatterns, testHostName);
+            assert.equal(matchingPattern, "www.uic.edu");
+            done();
+        });
+
+        it("Negative case: input 'nope.uic.edu' gives no pattern", function (done) {
+
+            const testHostName = "nope.uic.edu";
+            const matchingPattern = domainMatcherLib.matchHostName(testPatterns, testHostName);
+            assert.equal(matchingPattern, undefined);
+            done();
+        });
+    });
+
+    describe("Wildcard matches", function () {
+
+        it("Positive case: input 'www.example.com' gives pattern '*.example.com'", function (done) {
+
+            const testHostName = "www.example.com";
+            const matchingPattern = domainMatcherLib.matchHostName(testPatterns, testHostName);
+            assert.equal(matchingPattern, "*.example.com");
+            done();
+        });
+
+        it("Negative case: input 'www.example.com.co.uk' gives no pattern", function (done) {
+
+            const testHostName = "www.example.com.co.uk";
+            const matchingPattern = domainMatcherLib.matchHostName(testPatterns, testHostName);
+            assert.equal(matchingPattern, undefined);
+            done();
+        });
+    });
+
+    describe("Collapsed matches", function () {
+
+        it("Positive case: input 'example.com' gives pattern '*.example.com'", function (done) {
+            const testHostName = "example.com";
+            const matchingPattern = domainMatcherLib.matchHostName(testPatterns, testHostName);
+            assert.equal(matchingPattern, "*.example.com");
+            done();
+        });
+    });
+});
\ No newline at end of file