When you "pipe" input from one process or job to another, you are also spawning another process. Both of these jobs are said to be "children" of main process which called them (usually the process running the terminal emulator).
# 2 process are run here
# first job: echo
# second job: ls
echo "/home/" | ls
Looking at the source for zsh, it looks like when the cwd of a job differs from the original cwd of the job after execution, it will notify the user with a message like (pwd now: foo). This helps to ensure that the user knows exactly where the are in the directory tree, which is especially useful when they may not have intended to do so. Below is taken from jobs.c where the cwd (referred to as pwd) where among other things, the cwd (referenced as pwd) are compared before printing the (pwd now: foo) message. If they are different the message is printed, if they are equal it is not.
if ((lng & 4) || (interact && job == thisjob &&
jn->pwd && strcmp(jn->pwd, pwd))) {
doneprint = 1;
fprintf(fout, "(pwd %s: ", (lng & 4) ? "" : "now");
fprintdir(((lng & 4) && jn->pwd) ? jn->pwd : pwd, fout);
fprintf(fout, ")\n");
fflush(fout);
}
When you pipe something into ch you are changing the cwd in a child process and some of the normal checks which hide this message when calling cd directly are bipassed. Otherwise cd words in the same way.
# changes directories into ./test
echo "test" | cd
# (pwd now: /path/to/test)
# changes directory to home
echo | cd
# (pwd now: /home/<username>)
# calling this again will not echo the message, since the cwd is already
# the home directory and no change occurs
echo | cd
As for why the cwd is changed to the home directory (~) it is due to how cd behaves when no paths are given as an argument. Unlike a lot of linux commands, cd does not read from stdin for paths to move into. Due to this, piping into cd will simply populate stdin for cd, but that content is ignored. Because of this piping into cd is the same as just calling cd on its own.
# these next lines produce the same behavior
echo path/to/dir/test | cd
cd
When cd does not receive a path to move to, it will move you to your home directory (referenced on linux systems as ~)
# each of these lines produce the same behavior
cd /home/<username>
cd ~
cd