// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/resources/utils.js
// META: script=helpers.js

// The string "test" as ASCII bytes and base64url-encoded.
const test_bytes = new Uint8Array([0x74, 0x65, 0x73, 0x74]);
const test_b64 = "dGVzdA";

test(() => {
  let actual = PublicKeyCredential.parseCreationOptionsFromJSON({
    rp: {
      id: "example.com",
      name: "Example Inc",
    },
    user: {
      id: test_b64,
      name: "test@example.com",
      displayName: "test user"
    },
    challenge: test_b64,
    pubKeyCredParams: [
      {
        type: "public-key",
        alg: -7,
      },
    ],
  });
  let expected = {
    rp: {
      id: "example.com",
      name: "Example Inc",
    },
    user: {
      id: test_bytes,
      name: "test@example.com",
      displayName: "test user"
    },
    challenge: test_bytes,
    pubKeyCredParams: [
      {
        type: "public-key",
        alg: -7,
      },
    ],
    // The spec defaults the following fields:
    attestation: "none",
    hints: [],
  };

  assertJsonEquals(actual.rp, expected.rp);
  assert_true(userEntityEquals(actual.user, expected.user));
  assert_true(bytesEqual(actual.challenge, expected.challenge));
  assertJsonEquals(actual.pubKeyCredParams, expected.pubKeyCredParams, "pk");
  assert_equals(actual.attestation, expected.attestation);
  if (actual.hasOwnProperty('hints')) {
    // Not all implementations support hints yet.
    assertJsonEquals(actual.hints, expected.hints);
  }
}, "parseCreationOptionsFromJSON()");

test(() => {
  assert_throws_dom("EncodingError", () => {
    PublicKeyCredential.parseCreationOptionsFromJSON({
      rp: {
        id: "example.com",
        name: "Example Inc",
      },
      user: {
        id: "not valid base64url",
        name: "test@example.com",
        displayName: "test user"
      },
      challenge: "not valid base64url",
      pubKeyCredParams: [
        {
          type: "public-key",
          alg: -7,
        },
      ],
    });
  });
}, "parseCreationOptionsFromJSON() throws EncodingError");

test(() => {
  let actual = PublicKeyCredential.parseCreationOptionsFromJSON({
    rp: {
      id: "example.com",
      name: "Example Inc",
    },
    user: {
      id: test_b64,
      name: "test@example.com",
      displayName: "test user"
    },
    challenge: test_b64,
    pubKeyCredParams: [
      {
        type: "public-key",
        alg: -7,
      },
    ],
    extensions: {
      appidExclude: "https://example.com/appid",
      hmacCreateSecret: true,
      credentialProtectionPolicy: "userVerificationRequired",
      enforceCredentialProtectionPolicy: true,
      minPinLength: true,
      credProps: true,
      largeBlob: {
        support: "required",
        write: test_b64,
      },
      credBlob: test_b64,
      supplementalPubKeys: {
        scopes: ["spk scope"],
        attestation: "directest",
        attestationFormats: ["asn2"],
      },
      prf: {
        eval: {
          first: test_b64,
          second: test_b64,
        },
        evalByCredential: {
          "test cred": {
            first: test_b64,
            second: test_b64,
          },
        },
      },
    },
  });
  let expected = {
    rp: {
      id: "example.com",
      name: "Example Inc",
    },
    user: {
      id: test_bytes,
      name: "test@example.com",
      displayName: "test user"
    },
    challenge: test_bytes,
    pubKeyCredParams: [
      {
        type: "public-key",
        alg: -7,
      },
    ],
    extensions: {
      appidExclude: "https://example.com/appid",
      hmacCreateSecret: true,
      credentialProtectionPolicy: "userVerificationRequired",
      enforceCredentialProtectionPolicy: true,
      minPinLength: true,
      credProps: true,
      largeBlob: {
        support: "required",
        write: test_bytes,
      },
      credBlob: test_bytes,
      supplementalPubKeys: {
        scopes: ["spk scope"],
        attestation: "directest",
        attestationFormats: ["asn2"],
      },
      prf: {
        eval: {
          first: test_bytes,
          second: test_bytes,
        },
        evalByCredential: {
          "test cred": {
            first: test_bytes,
            second: test_bytes,
          },
        },
      },
    },
  };

  // Some implementations do not support all of these extensions.
  if (actual.extensions.hasOwnProperty('appidExclude')) {
    assert_equals(
        actual.extensions.appidExclude, expected.extensions.appidExclude);
  }
  if (actual.extensions.hasOwnProperty('hmacCreateSecret')) {
    assert_equals(
        actual.extensions.hmacCreateSecret,
        expected.extensions.hmacCreateSecret);
  }
  if (actual.extensions.hasOwnProperty('credentialProtectionPolicy')) {
    assert_equals(
        actual.extensions.credentialProtectionPolicy,
        expected.extensions.credentialProtectionPolicy);
  }
  if (actual.extensions.hasOwnProperty('enforceCredentialProtectionPolicy')) {
    assert_equals(
        actual.extensions.enforceCredentialProtectionPolicy,
        expected.extensions.enforceCredentialProtectionPolicy);
  }
  if (actual.extensions.hasOwnProperty('minPinLength')) {
    assert_equals(
        actual.extensions.minPinLength, expected.extensions.minPinLength);
  }
  if (actual.extensions.hasOwnProperty('credProps')) {
    assert_equals(actual.extensions.credProps, expected.extensions.credProps);
  }
  if (actual.extensions.hasOwnProperty('largeBlob')) {
    assert_equals(
        actual.extensions.largeBlob.support,
        expected.extensions.largeBlob.support);
    assert_true(bytesEqual(
        actual.extensions.largeBlob.write,
        expected.extensions.largeBlob.write));
  }
  if (actual.extensions.hasOwnProperty('credBlob')) {
    assert_true(
        bytesEqual(actual.extensions.credBlob, expected.extensions.credBlob));
  }
  if (actual.extensions.hasOwnProperty('supplementalPubKeys')) {
    assertJsonEquals(
        actual.extensions.supplementalPubKeys,
        expected.extensions.supplementalPubKeys);
  }
  if (actual.extensions.hasOwnProperty('prf')) {
    let prfValuesEquals = (a, b) => {
      return bytesEqual(a.first, b.first) && bytesEqual(a.second, b.second);
    };
    assert_true(
        prfValuesEquals(
            actual.extensions.prf.eval, expected.extensions.prf.eval),
        'prf eval');
    assert_true(
        prfValuesEquals(
            actual.extensions.prf.evalByCredential['test cred'],
            expected.extensions.prf.evalByCredential['test cred']),
        'prf ebc');
  }
}, "parseCreationOptionsFromJSON() with extensions");
