diff --git a/homeassistant/components/fitbit/api.py b/homeassistant/components/fitbit/api.py index ceb619c4385..49e51a0fd98 100644 --- a/homeassistant/components/fitbit/api.py +++ b/homeassistant/components/fitbit/api.py @@ -69,7 +69,7 @@ class FitbitApi(ABC): profile = response["user"] self._profile = FitbitProfile( encoded_id=profile["encodedId"], - full_name=profile["fullName"], + display_name=profile["displayName"], locale=profile.get("locale"), ) return self._profile diff --git a/homeassistant/components/fitbit/config_flow.py b/homeassistant/components/fitbit/config_flow.py index dd7e79e2c65..7ef6ecbfa28 100644 --- a/homeassistant/components/fitbit/config_flow.py +++ b/homeassistant/components/fitbit/config_flow.py @@ -90,7 +90,7 @@ class OAuth2FlowHandler( await self.async_set_unique_id(profile.encoded_id) self._abort_if_unique_id_configured() - return self.async_create_entry(title=profile.full_name, data=data) + return self.async_create_entry(title=profile.display_name, data=data) async def async_step_import(self, data: dict[str, Any]) -> FlowResult: """Handle import from YAML.""" diff --git a/homeassistant/components/fitbit/model.py b/homeassistant/components/fitbit/model.py index 38b1d0bb786..cd8ece163a4 100644 --- a/homeassistant/components/fitbit/model.py +++ b/homeassistant/components/fitbit/model.py @@ -14,8 +14,8 @@ class FitbitProfile: encoded_id: str """The ID representing the Fitbit user.""" - full_name: str - """The first name value specified in the user's account settings.""" + display_name: str + """The name shown when the user's friends look at their Fitbit profile.""" locale: str | None """The locale defined in the user's Fitbit account settings.""" diff --git a/tests/components/fitbit/conftest.py b/tests/components/fitbit/conftest.py index 682fb0edd3b..a076be7f63d 100644 --- a/tests/components/fitbit/conftest.py +++ b/tests/components/fitbit/conftest.py @@ -32,6 +32,15 @@ PROFILE_USER_ID = "fitbit-api-user-id-1" FAKE_ACCESS_TOKEN = "some-access-token" FAKE_REFRESH_TOKEN = "some-refresh-token" FAKE_AUTH_IMPL = "conftest-imported-cred" +FULL_NAME = "First Last" +DISPLAY_NAME = "First L." +PROFILE_DATA = { + "fullName": FULL_NAME, + "displayName": DISPLAY_NAME, + "displayNameSetting": "name", + "firstName": "First", + "lastName": "Last", +} PROFILE_API_URL = "https://api.fitbit.com/1/user/-/profile.json" DEVICES_API_URL = "https://api.fitbit.com/1/user/-/devices.json" @@ -214,20 +223,34 @@ def mock_profile_locale() -> str: return "en_US" +@pytest.fixture(name="profile_data") +def mock_profile_data() -> dict[str, Any]: + """Fixture to return other profile data fields.""" + return PROFILE_DATA + + +@pytest.fixture(name="profile_response") +def mock_profile_response( + profile_id: str, profile_locale: str, profile_data: dict[str, Any] +) -> dict[str, Any]: + """Fixture to construct the fake profile API response.""" + return { + "user": { + "encodedId": profile_id, + "locale": profile_locale, + **profile_data, + }, + } + + @pytest.fixture(name="profile", autouse=True) -def mock_profile(requests_mock: Mocker, profile_id: str, profile_locale: str) -> None: +def mock_profile(requests_mock: Mocker, profile_response: dict[str, Any]) -> None: """Fixture to setup fake requests made to Fitbit API during config flow.""" requests_mock.register_uri( "GET", PROFILE_API_URL, status_code=HTTPStatus.OK, - json={ - "user": { - "encodedId": profile_id, - "fullName": "My name", - "locale": profile_locale, - }, - }, + json=profile_response, ) diff --git a/tests/components/fitbit/test_config_flow.py b/tests/components/fitbit/test_config_flow.py index d51379c9adc..78d20b0fb58 100644 --- a/tests/components/fitbit/test_config_flow.py +++ b/tests/components/fitbit/test_config_flow.py @@ -17,8 +17,10 @@ from homeassistant.helpers import config_entry_oauth2_flow, issue_registry as ir from .conftest import ( CLIENT_ID, + DISPLAY_NAME, FAKE_AUTH_IMPL, PROFILE_API_URL, + PROFILE_DATA, PROFILE_USER_ID, SERVER_ACCESS_TOKEN, ) @@ -76,7 +78,7 @@ async def test_full_flow( entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 config_entry = entries[0] - assert config_entry.title == "My name" + assert config_entry.title == DISPLAY_NAME assert config_entry.unique_id == PROFILE_USER_ID data = dict(config_entry.data) @@ -286,7 +288,7 @@ async def test_import_fitbit_config( # Verify valid profile can be fetched from the API config_entry = entries[0] - assert config_entry.title == "My name" + assert config_entry.title == DISPLAY_NAME assert config_entry.unique_id == PROFILE_USER_ID data = dict(config_entry.data) @@ -598,3 +600,60 @@ async def test_reauth_wrong_user_id( assert result.get("reason") == "wrong_account" assert len(mock_setup.mock_calls) == 0 + + +@pytest.mark.parametrize( + ("profile_data", "expected_title"), + [ + (PROFILE_DATA, DISPLAY_NAME), + ({"displayName": DISPLAY_NAME}, DISPLAY_NAME), + ], + ids=("full_profile_data", "display_name_only"), +) +async def test_partial_profile_data( + hass: HomeAssistant, + hass_client_no_auth: ClientSessionGenerator, + aioclient_mock: AiohttpClientMocker, + current_request_with_host: None, + profile: None, + setup_credentials: None, + expected_title: str, +) -> None: + """Check full flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URL, + }, + ) + assert result["type"] == FlowResultType.EXTERNAL_STEP + assert result["url"] == ( + f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URL}" + f"&state={state}" + "&scope=activity+heartrate+nutrition+profile+settings+sleep+weight&prompt=consent" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + + aioclient_mock.post( + OAUTH2_TOKEN, + json=SERVER_ACCESS_TOKEN, + ) + + with patch( + "homeassistant.components.fitbit.async_setup_entry", return_value=True + ) as mock_setup: + await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert len(mock_setup.mock_calls) == 1 + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + config_entry = entries[0] + assert config_entry.title == expected_title